177 lines
6.2 KiB
HTML
177 lines
6.2 KiB
HTML
<section
|
|
appThemeNode="dmChatSurface"
|
|
class="chat-layout relative h-full min-w-0 overflow-x-hidden bg-background"
|
|
>
|
|
<header
|
|
appThemeNode="dmChatHeader"
|
|
class="flex h-14 shrink-0 items-center gap-3 border-b border-border px-4"
|
|
>
|
|
@if (peerUser()) {
|
|
<button
|
|
type="button"
|
|
class="flex min-w-0 flex-1 items-center gap-3 rounded-md py-1 pr-2 text-left transition-colors hover:bg-secondary/60 focus:outline-none focus:ring-2 focus:ring-primary/50"
|
|
[attr.aria-label]="'Open profile for ' + peerName()"
|
|
[title]="'Open profile for ' + peerName()"
|
|
(click)="openHeaderProfileCard($event)"
|
|
>
|
|
<app-user-avatar
|
|
[name]="peerName()"
|
|
[avatarUrl]="peerUser()?.avatarUrl"
|
|
[status]="peerUser()?.status"
|
|
[showStatusBadge]="true"
|
|
size="md"
|
|
/>
|
|
<div class="min-w-0 flex-1">
|
|
<h1 class="truncate text-base font-semibold text-foreground">{{ peerName() }}</h1>
|
|
<p class="text-xs text-muted-foreground">Direct Message</p>
|
|
</div>
|
|
</button>
|
|
} @else {
|
|
<app-user-avatar
|
|
[name]="peerName()"
|
|
[avatarUrl]="peerUser()?.avatarUrl"
|
|
[status]="peerUser()?.status"
|
|
[showStatusBadge]="true"
|
|
size="md"
|
|
/>
|
|
<div class="min-w-0 flex-1">
|
|
<h1 class="truncate text-base font-semibold text-foreground">{{ peerName() }}</h1>
|
|
<p class="text-xs text-muted-foreground">{{ isGroupConversation() ? 'Group Chat' : 'Direct Message' }}</p>
|
|
</div>
|
|
}
|
|
@if (showCallButton() && conversation()) {
|
|
<button
|
|
type="button"
|
|
class="grid h-9 w-9 place-items-center rounded-md bg-emerald-500 text-white transition-colors hover:bg-emerald-600 disabled:opacity-50"
|
|
[disabled]="!canCallConversation()"
|
|
[attr.aria-label]="'Call ' + peerName()"
|
|
[title]="'Call ' + peerName()"
|
|
(click)="callConversation()"
|
|
>
|
|
<ng-icon
|
|
[name]="peerCallIcon()"
|
|
class="h-4 w-4"
|
|
/>
|
|
</button>
|
|
}
|
|
</header>
|
|
|
|
@if (conversation()) {
|
|
<div
|
|
appThemeNode="dmMessageRegion"
|
|
class="absolute inset-x-0 bottom-0 top-14"
|
|
>
|
|
<app-chat-message-list
|
|
[allMessages]="chatMessages()"
|
|
[channelMessages]="chatMessages()"
|
|
[loading]="false"
|
|
[syncing]="false"
|
|
[currentUserId]="currentUserId()"
|
|
[isAdmin]="false"
|
|
[bottomPadding]="composerBottomPadding()"
|
|
[conversationKey]="conversationKey()"
|
|
[userLookupOverrides]="participantUsers()"
|
|
(replyRequested)="setReplyTo($event)"
|
|
(deleteRequested)="handleDeleteRequested($event)"
|
|
(editSaved)="handleEditSaved($event)"
|
|
(reactionAdded)="handleReactionAdded($event)"
|
|
(reactionToggled)="handleReactionToggled($event)"
|
|
(downloadRequested)="downloadAttachment($event)"
|
|
(imageOpened)="openLightbox($event)"
|
|
(imageGalleryOpened)="openImageGallery($event)"
|
|
(imageContextMenuRequested)="openImageContextMenu($event)"
|
|
(embedRemoved)="handleEmbedRemoved($event)"
|
|
/>
|
|
|
|
@for (messageStatus of messageStatuses(); track messageStatus.id) {
|
|
<span
|
|
data-testid="message-status"
|
|
class="sr-only"
|
|
>{{ messageStatus.status }}</span
|
|
>
|
|
}
|
|
</div>
|
|
|
|
<div
|
|
appThemeNode="chatComposerBar"
|
|
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
|
|
data-testid="dm-typing-indicator"
|
|
class="px-4 pb-1 text-xs text-muted-foreground"
|
|
>
|
|
{{ typingUsers().join(', ') }} {{ typingUsers().length === 1 ? 'is' : 'are' }} typing...
|
|
</div>
|
|
}
|
|
|
|
<app-chat-message-composer
|
|
[replyTo]="replyTo()"
|
|
[showKlipyGifPicker]="showGifPicker()"
|
|
[klipyEnabled]="klipyEnabled()"
|
|
[klipySignalSource]="null"
|
|
[textareaTestId]="'dm-input'"
|
|
[commandSurface]="'direct'"
|
|
(messageSubmitted)="handleMessageSubmitted($event)"
|
|
(typingStarted)="handleTypingStarted()"
|
|
(replyCleared)="clearReply()"
|
|
(heightChanged)="composerBottomPadding.set($event + 20)"
|
|
(klipyGifPickerToggleRequested)="toggleGifPicker()"
|
|
/>
|
|
</div>
|
|
|
|
@if (showGifPicker()) {
|
|
@if (isMobile()) {
|
|
<app-bottom-sheet (dismissed)="closeGifPicker()">
|
|
<div appThemeNode="chatGifPickerSurface">
|
|
<app-klipy-gif-picker
|
|
(gifSelected)="handleGifSelected($event)"
|
|
(closed)="closeGifPicker()"
|
|
/>
|
|
</div>
|
|
</app-bottom-sheet>
|
|
} @else {
|
|
<div
|
|
class="fixed inset-0 z-[89]"
|
|
tabindex="0"
|
|
role="button"
|
|
aria-label="Close GIF picker"
|
|
(click)="closeGifPicker()"
|
|
(keydown.enter)="closeGifPicker()"
|
|
(keydown.space)="closeGifPicker()"
|
|
></div>
|
|
|
|
<div class="pointer-events-none fixed inset-0 z-[90]">
|
|
<div
|
|
appThemeNode="chatGifPickerSurface"
|
|
class="pointer-events-auto absolute w-[calc(100vw-2rem)] max-w-5xl sm:w-[34rem] md:w-[42rem] xl:w-[52rem]"
|
|
[style.bottom.px]="composerBottomPadding() + 8"
|
|
[style.right.px]="gifPickerAnchorRight()"
|
|
>
|
|
<app-klipy-gif-picker
|
|
(gifSelected)="handleGifSelected($event)"
|
|
(closed)="closeGifPicker()"
|
|
/>
|
|
</div>
|
|
</div>
|
|
}
|
|
}
|
|
|
|
<app-chat-message-overlays
|
|
[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 {
|
|
<div class="flex flex-1 items-center justify-center px-6 text-sm text-muted-foreground">Select a direct message from the rail.</div>
|
|
}
|
|
</section>
|