feat: Add deafen to pc, fix mobiel view, fix freeze on startup
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<section
|
||||
appThemeNode="dmChatSurface"
|
||||
class="chat-layout relative h-full bg-background"
|
||||
class="chat-layout relative h-full min-w-0 overflow-x-hidden bg-background"
|
||||
>
|
||||
<header
|
||||
appThemeNode="dmChatHeader"
|
||||
@@ -78,6 +78,7 @@
|
||||
(reactionToggled)="handleReactionToggled($event)"
|
||||
(downloadRequested)="downloadAttachment($event)"
|
||||
(imageOpened)="openLightbox($event)"
|
||||
(imageGalleryOpened)="openImageGallery($event)"
|
||||
(imageContextMenuRequested)="openImageContextMenu($event)"
|
||||
(embedRemoved)="handleEmbedRemoved($event)"
|
||||
/>
|
||||
@@ -93,7 +94,7 @@
|
||||
|
||||
<div
|
||||
appThemeNode="chatComposerBar"
|
||||
class="chat-bottom-bar absolute bottom-0 left-0 right-2 z-10 bg-background/85 backdrop-blur-md"
|
||||
class="chat-bottom-bar absolute bottom-0 left-0 right-0 z-10 min-w-0 bg-background/85 backdrop-blur-md"
|
||||
>
|
||||
@if (typingUsers().length > 0) {
|
||||
<div
|
||||
@@ -156,12 +157,16 @@
|
||||
}
|
||||
|
||||
<app-chat-message-overlays
|
||||
[lightboxAttachment]="lightboxAttachment()"
|
||||
[lightboxState]="lightboxState()"
|
||||
[galleryAttachments]="galleryAttachments()"
|
||||
[imageContextMenu]="imageContextMenu()"
|
||||
(lightboxClosed)="closeLightbox()"
|
||||
(lightboxStepRequested)="stepLightbox($event)"
|
||||
(galleryClosed)="closeImageGallery()"
|
||||
(contextMenuClosed)="closeImageContextMenu()"
|
||||
(downloadRequested)="downloadAttachment($event)"
|
||||
(copyRequested)="copyImageToClipboard($event)"
|
||||
(imageOpened)="openLightbox($event)"
|
||||
(imageContextMenuRequested)="openImageContextMenu($event)"
|
||||
/>
|
||||
} @else {
|
||||
|
||||
@@ -45,6 +45,8 @@ import {
|
||||
LinkMetadataService,
|
||||
type ChatMessageEmbedRemoveEvent
|
||||
} from '../../../chat';
|
||||
import { stepLightboxIndex } from '../../../chat/domain/rules/chat-message-lightbox.rules';
|
||||
import { ChatLightboxState, ChatMessageImageLightboxEvent } from '../../../chat/feature/chat-messages/models/chat-messages.model';
|
||||
import type {
|
||||
DirectMessageStatus,
|
||||
LinkMetadata,
|
||||
@@ -102,7 +104,8 @@ export class DmChatComponent {
|
||||
readonly gifPickerAnchorRight = signal(16);
|
||||
readonly linkMetadataByMessageId = signal<Record<string, LinkMetadata[]>>({});
|
||||
readonly replyTo = signal<Message | null>(null);
|
||||
readonly lightboxAttachment = signal<Attachment | null>(null);
|
||||
readonly lightboxState = signal<ChatLightboxState | null>(null);
|
||||
readonly galleryAttachments = signal<Attachment[] | null>(null);
|
||||
readonly imageContextMenu = signal<ChatMessageImageContextMenuEvent | null>(null);
|
||||
readonly routeConversationId = toSignal(this.route.paramMap.pipe(map((params) => params.get('conversationId'))), {
|
||||
initialValue: this.route.snapshot.paramMap.get('conversationId')
|
||||
@@ -395,14 +398,55 @@ export class DmChatComponent {
|
||||
}));
|
||||
}
|
||||
|
||||
openLightbox(attachment: Attachment): void {
|
||||
if (attachment.available && attachment.objectUrl) {
|
||||
this.lightboxAttachment.set(attachment);
|
||||
openLightbox(event: ChatMessageImageLightboxEvent): void {
|
||||
const attachments = event.attachments.filter((attachment) => attachment.available && attachment.objectUrl);
|
||||
const index = attachments.findIndex((attachment) => attachment.id === event.attachment.id);
|
||||
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.lightboxState.set({
|
||||
attachments,
|
||||
index
|
||||
});
|
||||
}
|
||||
|
||||
closeLightbox(): void {
|
||||
this.lightboxAttachment.set(null);
|
||||
this.lightboxState.set(null);
|
||||
}
|
||||
|
||||
stepLightbox(delta: number): void {
|
||||
const state = this.lightboxState();
|
||||
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextIndex = stepLightboxIndex(state.index, delta, state.attachments.length);
|
||||
|
||||
if (nextIndex === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.lightboxState.set({
|
||||
attachments: state.attachments,
|
||||
index: nextIndex
|
||||
});
|
||||
}
|
||||
|
||||
openImageGallery(attachments: Attachment[]): void {
|
||||
const availableImages = attachments.filter((attachment) => attachment.available && attachment.objectUrl);
|
||||
|
||||
if (availableImages.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.galleryAttachments.set(availableImages);
|
||||
}
|
||||
|
||||
closeImageGallery(): void {
|
||||
this.galleryAttachments.set(null);
|
||||
}
|
||||
|
||||
openImageContextMenu(event: ChatMessageImageContextMenuEvent): void {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<swiper-slide class="block h-full w-full">
|
||||
<div class="flex h-full w-full min-h-0 overflow-hidden">
|
||||
<app-servers-rail class="block h-full shrink-0" />
|
||||
<div class="flex min-h-0 flex-1 overflow-hidden border-l border-border">
|
||||
<div class="flex min-h-0 min-w-0 flex-1 overflow-hidden border-l border-border">
|
||||
<app-dm-conversations-panel
|
||||
(conversationSelected)="setMobilePage('chat')"
|
||||
class="block h-full w-full"
|
||||
|
||||
@@ -60,24 +60,4 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@if (isMobile()) {
|
||||
<swiper-container
|
||||
class="block h-full min-h-0 w-full bg-background"
|
||||
slides-per-view="1"
|
||||
space-between="0"
|
||||
initial-slide="0"
|
||||
threshold="10"
|
||||
resistance-ratio="0"
|
||||
>
|
||||
<swiper-slide class="block h-full w-full">
|
||||
<div class="flex h-full w-full min-h-0 overflow-hidden">
|
||||
<app-servers-rail class="block h-full shrink-0" />
|
||||
<div class="flex min-h-0 flex-1 overflow-hidden border-l border-border">
|
||||
<ng-container [ngTemplateOutlet]="pageContent" />
|
||||
</div>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
</swiper-container>
|
||||
} @else {
|
||||
<ng-container [ngTemplateOutlet]="pageContent" />
|
||||
}
|
||||
<ng-container [ngTemplateOutlet]="pageContent" />
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { FindPeopleComponent } from './find-people.component';
|
||||
import { ViewportService } from '../../../../core/platform';
|
||||
import { selectAllUsers } from '../../../../store/users/users.selectors';
|
||||
import { selectSavedRooms } from '../../../../store/rooms/rooms.selectors';
|
||||
import type { User, Room } from '../../../../shared-kernel';
|
||||
@@ -21,7 +20,6 @@ import type { User, Room } from '../../../../shared-kernel';
|
||||
interface HarnessOptions {
|
||||
users?: User[];
|
||||
saved?: Room[];
|
||||
isMobile?: boolean;
|
||||
}
|
||||
|
||||
function createHarness(options: HarnessOptions = {}) {
|
||||
@@ -44,8 +42,7 @@ function createHarness(options: HarnessOptions = {}) {
|
||||
const injector = Injector.create({
|
||||
providers: [
|
||||
FindPeopleComponent,
|
||||
{ provide: Store, useValue: store },
|
||||
{ provide: ViewportService, useValue: { isMobile: signal(options.isMobile ?? false) } }
|
||||
{ provide: Store, useValue: store }
|
||||
]
|
||||
});
|
||||
const component = runInInjectionContext(injector, () => injector.get(FindPeopleComponent));
|
||||
@@ -54,11 +51,6 @@ function createHarness(options: HarnessOptions = {}) {
|
||||
}
|
||||
|
||||
describe('FindPeopleComponent', () => {
|
||||
it('exposes the mobile viewport flag', () => {
|
||||
expect(createHarness().component.isMobile()).toBe(false);
|
||||
expect(createHarness({ isMobile: true }).component.isMobile()).toBe(true);
|
||||
});
|
||||
|
||||
it('has no discoverable people for a brand-new account', () => {
|
||||
const { component } = createHarness();
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import {
|
||||
CUSTOM_ELEMENTS_SCHEMA,
|
||||
Component,
|
||||
computed,
|
||||
inject,
|
||||
@@ -18,16 +17,13 @@ import {
|
||||
} from '@ng-icons/lucide';
|
||||
|
||||
import { UserSearchListComponent } from '../user-search-list/user-search-list.component';
|
||||
import { ServersRailComponent } from '../../../../features/servers/servers-rail/servers-rail.component';
|
||||
import { ViewportService } from '../../../../core/platform';
|
||||
import { selectAllUsers } from '../../../../store/users/users.selectors';
|
||||
import { selectSavedRooms } from '../../../../store/rooms/rooms.selectors';
|
||||
|
||||
/**
|
||||
* Dedicated people-discovery page. Wraps {@link UserSearchListComponent} with a search
|
||||
* field and an onboarding empty state for accounts that have not joined any servers yet.
|
||||
* On mobile the page is mounted inside a Swiper slide alongside the servers rail so the
|
||||
* primary navigation stays reachable, matching the chat-room and DM workspace layouts.
|
||||
* On mobile the global app-shell servers rail stays visible beside this page.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-find-people',
|
||||
@@ -37,18 +33,16 @@ import { selectSavedRooms } from '../../../../store/rooms/rooms.selectors';
|
||||
FormsModule,
|
||||
RouterLink,
|
||||
NgIcon,
|
||||
UserSearchListComponent,
|
||||
ServersRailComponent
|
||||
UserSearchListComponent
|
||||
],
|
||||
viewProviders: [provideIcons({ lucideArrowLeft, lucideSearch, lucideUsers })],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
templateUrl: './find-people.component.html'
|
||||
templateUrl: './find-people.component.html',
|
||||
host: {
|
||||
class: 'block h-full min-h-0 min-w-0 w-full overflow-hidden'
|
||||
}
|
||||
})
|
||||
export class FindPeopleComponent {
|
||||
private store = inject(Store);
|
||||
private readonly viewport = inject(ViewportService);
|
||||
|
||||
readonly isMobile = this.viewport.isMobile;
|
||||
searchQuery = signal('');
|
||||
private users = this.store.selectSignal(selectAllUsers);
|
||||
private savedRooms = this.store.selectSignal(selectSavedRooms);
|
||||
|
||||
Reference in New Issue
Block a user