@rtsee/ngx 0.0.112 → 0.0.114
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ngx/fesm2022/rtsee-ngx.mjs +542 -82
- package/dist/ngx/fesm2022/rtsee-ngx.mjs.map +1 -1
- package/dist/ngx/index.d.ts +49 -3
- package/dist/ngx/src/lib/theme/containers/nav.scss +2 -0
- package/dist/ngx/src/lib/theme/containers/shell.scss +14 -6
- package/dist/ngx/src/lib/theme/containers/variables.scss +1 -1
- package/dist/ngx/src/lib/theme/messenger/chat-input.scss +36 -0
- package/dist/ngx/src/lib/theme/messenger/chat.scss +90 -0
- package/dist/ngx/src/lib/theme/messenger/chats-list.scss +7 -0
- package/dist/ngx/src/lib/theme/messenger/create-chat.scss +71 -0
- package/dist/ngx/src/lib/theme/messenger/message.scss +14 -8
- package/dist/ngx/src/lib/theme/messenger/messages-list.scss +17 -2
- package/dist/ngx/src/lib/theme/messenger/messenger-header.scss +45 -14
- package/dist/ngx/src/lib/theme/messenger/profile.scss +68 -259
- package/package.json +11 -11
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Injectable, Input, Component, ViewChild, EventEmitter, Output, forwardRef, Directive, HostListener, reflectComponentType, DOCUMENT, Inject, ChangeDetectionStrategy, output, CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
|
3
3
|
import * as i2$1 from '@angular/common';
|
|
4
|
-
import { NgClass, NgIf, NgForOf, DatePipe, NgComponentOutlet, SlicePipe, JsonPipe, DOCUMENT as DOCUMENT$1, CommonModule, NgStyle, NgOptimizedImage } from '@angular/common';
|
|
4
|
+
import { NgClass, NgIf, NgForOf, DecimalPipe, DatePipe, NgComponentOutlet, SlicePipe, JsonPipe, DOCUMENT as DOCUMENT$1, CommonModule, NgStyle, NgOptimizedImage } from '@angular/common';
|
|
5
5
|
import * as i1 from '@angular/forms';
|
|
6
6
|
import { FormsModule, FormGroup, FormControl, Validators, ReactiveFormsModule } from '@angular/forms';
|
|
7
7
|
import { Select } from 'primeng/select';
|
|
@@ -10,23 +10,23 @@ import * as i4 from 'primeng/button';
|
|
|
10
10
|
import { Button, ButtonModule } from 'primeng/button';
|
|
11
11
|
import { interval, tap, map, filter } from 'rxjs';
|
|
12
12
|
import { RTSeeTextLabelKeys, RTSeeCore, hasPermission, RTSeePeerPermissions } from '@rtsee/core';
|
|
13
|
-
import * as i2$2 from 'primeng/message';
|
|
14
|
-
import { Message, MessageModule } from 'primeng/message';
|
|
15
13
|
import { AuthEndpointsKeys, LS_TOKEN_LABEL, LS_USER_LABEL, DEFAULT_ROUTE_NAMES } from '@rtsee/auth';
|
|
16
14
|
import * as i2 from '@angular/router';
|
|
17
15
|
import { RouterLink, RouterLinkActive, NavigationEnd } from '@angular/router';
|
|
18
|
-
import { Divider } from 'primeng/divider';
|
|
19
16
|
import * as i1$1 from '@angular/common/http';
|
|
20
17
|
import { RTSeeChatMemberRoles, RTSeeIllustratorEvents, RTSeeChatTypes, OzzyTaskStatus, getStoryTranslatorLanguage, STORY_TRANSLATOR_LANGUAGES } from '@rtsee/common';
|
|
21
18
|
import { sanitizeImageUrl, ManageAgentForm, RTSeeForm, RTSeeFormFieldRadioGroup, RTSeeFormField, RTSeeFormFieldImageUploader, fetchImageBlob, RTSeeRater } from '@rtsee/utilities-ui';
|
|
22
19
|
import { InputText } from 'primeng/inputtext';
|
|
23
20
|
import { InputGroup } from 'primeng/inputgroup';
|
|
21
|
+
import * as i2$2 from 'primeng/message';
|
|
22
|
+
import { Message, MessageModule } from 'primeng/message';
|
|
24
23
|
import { Textarea } from 'primeng/textarea';
|
|
25
24
|
import { Checkbox } from 'primeng/checkbox';
|
|
26
25
|
import { RadioButton } from 'primeng/radiobutton';
|
|
27
26
|
import { InputNumber } from 'primeng/inputnumber';
|
|
28
27
|
import * as i1$2 from 'primeng/autocomplete';
|
|
29
28
|
import { AutoCompleteModule } from 'primeng/autocomplete';
|
|
29
|
+
import { Divider } from 'primeng/divider';
|
|
30
30
|
import { ImageCropperComponent } from 'ngx-image-cropper';
|
|
31
31
|
import { Tooltip } from 'primeng/tooltip';
|
|
32
32
|
import { Fieldset } from 'primeng/fieldset';
|
|
@@ -51,7 +51,6 @@ import { Tabs, TabList, Tab, TabPanels, TabPanel } from 'primeng/tabs';
|
|
|
51
51
|
import { ColorPicker } from 'primeng/colorpicker';
|
|
52
52
|
import { Slider } from 'primeng/slider';
|
|
53
53
|
import { Tag } from 'primeng/tag';
|
|
54
|
-
import { Listbox } from 'primeng/listbox';
|
|
55
54
|
import { RTSeeRoutesList } from '@rtsee/factory';
|
|
56
55
|
import { Avatar } from 'primeng/avatar';
|
|
57
56
|
import { Image } from 'primeng/image';
|
|
@@ -284,21 +283,81 @@ class ProfileComponent {
|
|
|
284
283
|
this.RTSeeTextLabelKeys = RTSeeTextLabelKeys;
|
|
285
284
|
this.authConfiguration = this.authService.rtseeAuth.authConfiguration;
|
|
286
285
|
}
|
|
286
|
+
get targetPeer() {
|
|
287
|
+
if (this.peer) {
|
|
288
|
+
return { id: this.peer.clientId, name: this.peer.name, imageUrl: this.peer.imageUrl };
|
|
289
|
+
}
|
|
290
|
+
if (this.messenger?.openedChat) {
|
|
291
|
+
const myId = this.messenger.myPeer?.clientId || this.messenger.clientId;
|
|
292
|
+
const other = this.messenger.openedChat.members.find((m) => m.id !== myId && m.clientId !== myId);
|
|
293
|
+
if (other) {
|
|
294
|
+
return other;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (this.messenger?.myPeer) {
|
|
298
|
+
return {
|
|
299
|
+
id: this.messenger.myPeer.clientId,
|
|
300
|
+
name: this.messenger.myPeer.name,
|
|
301
|
+
imageUrl: this.messenger.myPeer.imageUrl,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
get isOtherUser() {
|
|
307
|
+
if (!this.messenger?.ownProfileOpened) {
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
const myId = this.messenger?.myPeer?.clientId || this.messenger?.clientId;
|
|
311
|
+
if (this.targetPeer && myId && this.targetPeer.id !== myId) {
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
get displayName() {
|
|
317
|
+
return (this.targetPeer?.name ||
|
|
318
|
+
this.messenger?.openedChat?.name ||
|
|
319
|
+
this.messenger?.myPeer?.name ||
|
|
320
|
+
'User');
|
|
321
|
+
}
|
|
322
|
+
get displayInitials() {
|
|
323
|
+
const name = this.displayName.trim();
|
|
324
|
+
if (!name)
|
|
325
|
+
return '?';
|
|
326
|
+
const parts = name.split(' ');
|
|
327
|
+
if (parts.length >= 2 && parts[1]) {
|
|
328
|
+
return (parts[0][0] + parts[1][0]).toUpperCase();
|
|
329
|
+
}
|
|
330
|
+
return name.slice(0, 2).toUpperCase();
|
|
331
|
+
}
|
|
332
|
+
get displayImageUrl() {
|
|
333
|
+
return this.targetPeer?.imageUrl || (this.isOtherUser ? undefined : this.messenger?.myPeer?.imageUrl);
|
|
334
|
+
}
|
|
335
|
+
get displaySubtitle() {
|
|
336
|
+
if (this.isOtherUser) {
|
|
337
|
+
return this.messenger?.openedChat ? 'Direct Chat' : 'Contact';
|
|
338
|
+
}
|
|
339
|
+
if (!this.messenger?.myPeer?.name) {
|
|
340
|
+
return '';
|
|
341
|
+
}
|
|
342
|
+
return 'My Profile';
|
|
343
|
+
}
|
|
287
344
|
setLanguage() {
|
|
288
345
|
this.messenger.setLanguage(this.messenger.currentLanguage);
|
|
289
346
|
void this.messenger.propagateLanguageChange(this.messenger.currentLanguage);
|
|
290
347
|
}
|
|
291
348
|
call() {
|
|
292
|
-
|
|
349
|
+
const chat = this.messenger.openedChat;
|
|
350
|
+
if (!chat) {
|
|
293
351
|
return;
|
|
294
352
|
}
|
|
295
|
-
void this.messenger.initializeVideoCall(
|
|
353
|
+
void this.messenger.initializeVideoCall(chat);
|
|
296
354
|
}
|
|
297
355
|
videoCall() {
|
|
298
|
-
|
|
356
|
+
const chat = this.messenger.openedChat;
|
|
357
|
+
if (!chat) {
|
|
299
358
|
return;
|
|
300
359
|
}
|
|
301
|
-
void this.messenger.initializeVideoCall(
|
|
360
|
+
void this.messenger.initializeVideoCall(chat);
|
|
302
361
|
}
|
|
303
362
|
logOut() {
|
|
304
363
|
this.authService.rtseeAuth.logOut();
|
|
@@ -308,11 +367,11 @@ class ProfileComponent {
|
|
|
308
367
|
}, 200);
|
|
309
368
|
}
|
|
310
369
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ProfileComponent, deps: [{ token: RTSeeAuthService }, { token: i2.Router }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
311
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: ProfileComponent, isStandalone: true, selector: "rtsee-profile", inputs: { messenger: "messenger", peer: "peer" }, ngImport: i0, template: "<div class=\"rts-messenger-profile\">\n @if (
|
|
370
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: ProfileComponent, isStandalone: true, selector: "rtsee-profile", inputs: { messenger: "messenger", peer: "peer" }, ngImport: i0, template: "<div class=\"rts-chat-info rts-messenger-profile\">\n @if (displaySubtitle) {\n <div class=\"rts-chat-info-meta\">\n <p class=\"rts-chat-info-type\">{{ displaySubtitle }}</p>\n </div>\n }\n\n @if (isOtherUser) {\n <div class=\"rts-chat-info-call-actions\">\n <button class=\"rts-call-btn\"\n (click)=\"call()\"\n type=\"button\"\n title=\"Audio Call\"\n >\n <span class=\"material-icons-outlined\">call</span>\n <span>Audio</span>\n </button>\n <button class=\"rts-call-btn rts-call-btn-video\"\n (click)=\"videoCall()\"\n type=\"button\"\n title=\"Video Call\"\n >\n <span class=\"material-icons-outlined\">videocam</span>\n <span>Video</span>\n </button>\n </div>\n }\n\n @if (!isOtherUser && messenger.myPeer && !messenger.myPeer.isAnonymous) {\n <div class=\"rts-chat-info-actions\">\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n (click)=\"messenger.openManageProfile()\"\n >\n <span class=\"material-icons-outlined\">edit</span>\n {{ messenger.labels[RTSeeTextLabelKeys.MESSENGER_EDIT_PROFILE_BTN_LABEL] || 'Edit Profile' }}\n </button>\n </div>\n\n <hr class=\"rts-divider\" />\n\n <div class=\"rts-profile-section\">\n <div class=\"rts-profile-field-row\">\n <label class=\"rts-profile-field-label\">Language</label>\n <p-select [options]=\"messenger.languageOptions\"\n [(ngModel)]=\"messenger.currentLanguage\"\n (ngModelChange)=\"setLanguage()\"\n optionLabel=\"key\"\n optionValue=\"key\"\n [placeholder]=\"messenger.labels[RTSeeTextLabelKeys.MESSANGER_LANGUAGE_PLACEHOLDER]\"\n class=\"rts-profile-language-select\"\n />\n </div>\n </div>\n\n @if (messenger.myPeer.plan) {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-section\">\n <div class=\"rts-profile-plan-badge\">\n <span class=\"material-icons-outlined\">verified</span>\n <span>{{ messenger.myPeer.plan.productName }}</span>\n </div>\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n (click)=\"messenger.openManagePlanPage()\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.MANAGE_PLAN] || 'Manage Plan' }}\n </button>\n </div>\n } @else {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-section\">\n <button class=\"rts-btn rts-btn-primary rts-btn-block\"\n (click)=\"messenger.openPaymentPage()\"\n >\n <span class=\"material-icons-outlined\">stars</span>\n {{ messenger.labels[RTSeeTextLabelKeys.SUBSCRIBE_LABEL] || 'Upgrade Plan' }}\n </button>\n </div>\n }\n\n <hr class=\"rts-divider\" />\n <div class=\"rts-chat-info-danger-zone\">\n <button class=\"rts-btn rts-btn-danger rts-btn-block\"\n (click)=\"logOut()\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.LOGOUT_BUTTON] || 'Logout' }}\n </button>\n </div>\n }\n\n @if (!isOtherUser && messenger.myPeer?.isAnonymous) {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-anon-box\">\n <div class=\"rts-profile-anon-icon\">\n <span class=\"material-icons-outlined\">shield</span>\n </div>\n <div class=\"rts-profile-anon-text\">\n @if (!messenger.currentLanguage || messenger.currentLanguage === 'eng') {\n <p>This is a temporary anonymous identity. Sign up or sign in to save your history permanently.</p>\n } @else if (messenger.currentLanguage === 'ua') {\n <p>\u0426\u0435 \u0442\u0438\u043C\u0447\u0430\u0441\u043E\u0432\u0430 \u0430\u043D\u043E\u043D\u0456\u043C\u043D\u0430 \u043E\u0441\u043E\u0431\u0438\u0441\u0442\u0456\u0441\u0442\u044C. \u0417\u0430\u0440\u0435\u0454\u0441\u0442\u0440\u0443\u0439\u0442\u0435\u0441\u044F, \u0430\u0431\u0438 \u0437\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u0456\u0441\u0442\u043E\u0440\u0456\u044E \u0441\u043F\u0456\u043B\u043A\u0443\u0432\u0430\u043D\u043D\u044F.</p>\n }\n </div>\n </div>\n\n <div class=\"rts-profile-auth-actions\">\n <button class=\"rts-btn rts-btn-primary rts-btn-block\"\n [routerLink]=\"authConfiguration.getRoutePathSplit(AuthEndpointsKeys.SIGN_UP)\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.SIGN_UP_BUTTON] || 'Sign Up' }}\n </button>\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n [routerLink]=\"authConfiguration.getRoutePathSplit(AuthEndpointsKeys.SIGN_IN)\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.SIGN_IN_BUTTON] || 'Sign In' }}\n </button>\n </div>\n }\n</div>\n", dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); }
|
|
312
371
|
}
|
|
313
372
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ProfileComponent, decorators: [{
|
|
314
373
|
type: Component,
|
|
315
|
-
args: [{ selector: 'rtsee-profile', imports: [
|
|
374
|
+
args: [{ selector: 'rtsee-profile', imports: [RouterLink, Select, FormsModule], standalone: true, template: "<div class=\"rts-chat-info rts-messenger-profile\">\n @if (displaySubtitle) {\n <div class=\"rts-chat-info-meta\">\n <p class=\"rts-chat-info-type\">{{ displaySubtitle }}</p>\n </div>\n }\n\n @if (isOtherUser) {\n <div class=\"rts-chat-info-call-actions\">\n <button class=\"rts-call-btn\"\n (click)=\"call()\"\n type=\"button\"\n title=\"Audio Call\"\n >\n <span class=\"material-icons-outlined\">call</span>\n <span>Audio</span>\n </button>\n <button class=\"rts-call-btn rts-call-btn-video\"\n (click)=\"videoCall()\"\n type=\"button\"\n title=\"Video Call\"\n >\n <span class=\"material-icons-outlined\">videocam</span>\n <span>Video</span>\n </button>\n </div>\n }\n\n @if (!isOtherUser && messenger.myPeer && !messenger.myPeer.isAnonymous) {\n <div class=\"rts-chat-info-actions\">\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n (click)=\"messenger.openManageProfile()\"\n >\n <span class=\"material-icons-outlined\">edit</span>\n {{ messenger.labels[RTSeeTextLabelKeys.MESSENGER_EDIT_PROFILE_BTN_LABEL] || 'Edit Profile' }}\n </button>\n </div>\n\n <hr class=\"rts-divider\" />\n\n <div class=\"rts-profile-section\">\n <div class=\"rts-profile-field-row\">\n <label class=\"rts-profile-field-label\">Language</label>\n <p-select [options]=\"messenger.languageOptions\"\n [(ngModel)]=\"messenger.currentLanguage\"\n (ngModelChange)=\"setLanguage()\"\n optionLabel=\"key\"\n optionValue=\"key\"\n [placeholder]=\"messenger.labels[RTSeeTextLabelKeys.MESSANGER_LANGUAGE_PLACEHOLDER]\"\n class=\"rts-profile-language-select\"\n />\n </div>\n </div>\n\n @if (messenger.myPeer.plan) {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-section\">\n <div class=\"rts-profile-plan-badge\">\n <span class=\"material-icons-outlined\">verified</span>\n <span>{{ messenger.myPeer.plan.productName }}</span>\n </div>\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n (click)=\"messenger.openManagePlanPage()\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.MANAGE_PLAN] || 'Manage Plan' }}\n </button>\n </div>\n } @else {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-section\">\n <button class=\"rts-btn rts-btn-primary rts-btn-block\"\n (click)=\"messenger.openPaymentPage()\"\n >\n <span class=\"material-icons-outlined\">stars</span>\n {{ messenger.labels[RTSeeTextLabelKeys.SUBSCRIBE_LABEL] || 'Upgrade Plan' }}\n </button>\n </div>\n }\n\n <hr class=\"rts-divider\" />\n <div class=\"rts-chat-info-danger-zone\">\n <button class=\"rts-btn rts-btn-danger rts-btn-block\"\n (click)=\"logOut()\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.LOGOUT_BUTTON] || 'Logout' }}\n </button>\n </div>\n }\n\n @if (!isOtherUser && messenger.myPeer?.isAnonymous) {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-anon-box\">\n <div class=\"rts-profile-anon-icon\">\n <span class=\"material-icons-outlined\">shield</span>\n </div>\n <div class=\"rts-profile-anon-text\">\n @if (!messenger.currentLanguage || messenger.currentLanguage === 'eng') {\n <p>This is a temporary anonymous identity. Sign up or sign in to save your history permanently.</p>\n } @else if (messenger.currentLanguage === 'ua') {\n <p>\u0426\u0435 \u0442\u0438\u043C\u0447\u0430\u0441\u043E\u0432\u0430 \u0430\u043D\u043E\u043D\u0456\u043C\u043D\u0430 \u043E\u0441\u043E\u0431\u0438\u0441\u0442\u0456\u0441\u0442\u044C. \u0417\u0430\u0440\u0435\u0454\u0441\u0442\u0440\u0443\u0439\u0442\u0435\u0441\u044F, \u0430\u0431\u0438 \u0437\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u0456\u0441\u0442\u043E\u0440\u0456\u044E \u0441\u043F\u0456\u043B\u043A\u0443\u0432\u0430\u043D\u043D\u044F.</p>\n }\n </div>\n </div>\n\n <div class=\"rts-profile-auth-actions\">\n <button class=\"rts-btn rts-btn-primary rts-btn-block\"\n [routerLink]=\"authConfiguration.getRoutePathSplit(AuthEndpointsKeys.SIGN_UP)\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.SIGN_UP_BUTTON] || 'Sign Up' }}\n </button>\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n [routerLink]=\"authConfiguration.getRoutePathSplit(AuthEndpointsKeys.SIGN_IN)\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.SIGN_IN_BUTTON] || 'Sign In' }}\n </button>\n </div>\n }\n</div>\n" }]
|
|
316
375
|
}], ctorParameters: () => [{ type: RTSeeAuthService }, { type: i2.Router }], propDecorators: { messenger: [{
|
|
317
376
|
type: Input
|
|
318
377
|
}], peer: [{
|
|
@@ -1356,15 +1415,27 @@ class ChatInfoComponent {
|
|
|
1356
1415
|
onEditChatInfo() {
|
|
1357
1416
|
this.editChatInfo.emit();
|
|
1358
1417
|
}
|
|
1418
|
+
call() {
|
|
1419
|
+
if (!this.chatInfo?.chat) {
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
void this.messenger.initializeVideoCall(this.chatInfo.chat);
|
|
1423
|
+
}
|
|
1424
|
+
videoCall() {
|
|
1425
|
+
if (!this.chatInfo?.chat) {
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
void this.messenger.initializeVideoCall(this.chatInfo.chat);
|
|
1429
|
+
}
|
|
1359
1430
|
async onDeleteChat() {
|
|
1360
1431
|
await this.chatInfo.deleteChat();
|
|
1361
1432
|
}
|
|
1362
1433
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ChatInfoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1363
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: ChatInfoComponent, isStandalone: true, selector: "rtsee-chat-info", inputs: { messenger: "messenger", chatInfo: "chatInfo" }, outputs: { editChatInfo: "editChatInfo", chatDeleted: "chatDeleted" }, ngImport: i0, template: "<div class=\"rts-chat-info\">\n <div class=\"rts-chat-info-meta\">\n <p class=\"rts-chat-info-type\">{{ chatInfo.chat.type }}</p>\n <p class=\"rts-chat-info-member-count\">{{ chatInfo.chat.members.length }} members</p>\n </div>\n\n @if (chatInfo.canManageChatInfo) {\n <div class=\"rts-chat-info-actions\">\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n (click)=\"onEditChatInfo()\"\n >\n <span class=\"material-icons-outlined\">edit</span>\n Edit Chat Info\n </button>\n </div>\n }\n\n <hr class=\"rts-divider\" />\n\n <rtsee-chat-members\n [chatInfo]=\"chatInfo\"\n [messenger]=\"messenger\"\n ></rtsee-chat-members>\n\n @if (chatInfo.myRole === RTSeeChatMemberRoles.ADMIN) {\n <hr class=\"rts-divider\" />\n <div class=\"rts-chat-info-danger-zone\">\n <button class=\"rts-btn rts-btn-danger rts-btn-block\"\n (click)=\"onDeleteChat()\"\n >\n Delete Chat\n </button>\n </div>\n }\n</div>\n", dependencies: [{ kind: "component", type: ChatMembersComponent, selector: "rtsee-chat-members", inputs: ["chatInfo", "messenger"] }] }); }
|
|
1434
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: ChatInfoComponent, isStandalone: true, selector: "rtsee-chat-info", inputs: { messenger: "messenger", chatInfo: "chatInfo" }, outputs: { editChatInfo: "editChatInfo", chatDeleted: "chatDeleted" }, ngImport: i0, template: "<div class=\"rts-chat-info\">\n <div class=\"rts-chat-info-meta\">\n <p class=\"rts-chat-info-type\">{{ chatInfo.chat.type }}</p>\n <p class=\"rts-chat-info-member-count\">{{ chatInfo.chat.members.length }} members</p>\n </div>\n\n <div class=\"rts-chat-info-call-actions\">\n <button class=\"rts-call-btn\"\n (click)=\"call()\"\n type=\"button\"\n title=\"Audio Call\"\n >\n <span class=\"material-icons-outlined\">call</span>\n <span>Audio</span>\n </button>\n <button class=\"rts-call-btn rts-call-btn-video\"\n (click)=\"videoCall()\"\n type=\"button\"\n title=\"Video Call\"\n >\n <span class=\"material-icons-outlined\">videocam</span>\n <span>Video</span>\n </button>\n </div>\n\n @if (chatInfo.canManageChatInfo) {\n <div class=\"rts-chat-info-actions\">\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n (click)=\"onEditChatInfo()\"\n >\n <span class=\"material-icons-outlined\">edit</span>\n Edit Chat Info\n </button>\n </div>\n }\n\n <hr class=\"rts-divider\" />\n\n <rtsee-chat-members\n [chatInfo]=\"chatInfo\"\n [messenger]=\"messenger\"\n ></rtsee-chat-members>\n\n @if (chatInfo.myRole === RTSeeChatMemberRoles.ADMIN) {\n <hr class=\"rts-divider\" />\n <div class=\"rts-chat-info-danger-zone\">\n <button class=\"rts-btn rts-btn-danger rts-btn-block\"\n (click)=\"onDeleteChat()\"\n >\n Delete Chat\n </button>\n </div>\n }\n</div>\n", dependencies: [{ kind: "component", type: ChatMembersComponent, selector: "rtsee-chat-members", inputs: ["chatInfo", "messenger"] }] }); }
|
|
1364
1435
|
}
|
|
1365
1436
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ChatInfoComponent, decorators: [{
|
|
1366
1437
|
type: Component,
|
|
1367
|
-
args: [{ selector: 'rtsee-chat-info', standalone: true, imports: [PreloaderComponent, ChatMembersComponent], template: "<div class=\"rts-chat-info\">\n <div class=\"rts-chat-info-meta\">\n <p class=\"rts-chat-info-type\">{{ chatInfo.chat.type }}</p>\n <p class=\"rts-chat-info-member-count\">{{ chatInfo.chat.members.length }} members</p>\n </div>\n\n @if (chatInfo.canManageChatInfo) {\n <div class=\"rts-chat-info-actions\">\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n (click)=\"onEditChatInfo()\"\n >\n <span class=\"material-icons-outlined\">edit</span>\n Edit Chat Info\n </button>\n </div>\n }\n\n <hr class=\"rts-divider\" />\n\n <rtsee-chat-members\n [chatInfo]=\"chatInfo\"\n [messenger]=\"messenger\"\n ></rtsee-chat-members>\n\n @if (chatInfo.myRole === RTSeeChatMemberRoles.ADMIN) {\n <hr class=\"rts-divider\" />\n <div class=\"rts-chat-info-danger-zone\">\n <button class=\"rts-btn rts-btn-danger rts-btn-block\"\n (click)=\"onDeleteChat()\"\n >\n Delete Chat\n </button>\n </div>\n }\n</div>\n" }]
|
|
1438
|
+
args: [{ selector: 'rtsee-chat-info', standalone: true, imports: [PreloaderComponent, ChatMembersComponent], template: "<div class=\"rts-chat-info\">\n <div class=\"rts-chat-info-meta\">\n <p class=\"rts-chat-info-type\">{{ chatInfo.chat.type }}</p>\n <p class=\"rts-chat-info-member-count\">{{ chatInfo.chat.members.length }} members</p>\n </div>\n\n <div class=\"rts-chat-info-call-actions\">\n <button class=\"rts-call-btn\"\n (click)=\"call()\"\n type=\"button\"\n title=\"Audio Call\"\n >\n <span class=\"material-icons-outlined\">call</span>\n <span>Audio</span>\n </button>\n <button class=\"rts-call-btn rts-call-btn-video\"\n (click)=\"videoCall()\"\n type=\"button\"\n title=\"Video Call\"\n >\n <span class=\"material-icons-outlined\">videocam</span>\n <span>Video</span>\n </button>\n </div>\n\n @if (chatInfo.canManageChatInfo) {\n <div class=\"rts-chat-info-actions\">\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n (click)=\"onEditChatInfo()\"\n >\n <span class=\"material-icons-outlined\">edit</span>\n Edit Chat Info\n </button>\n </div>\n }\n\n <hr class=\"rts-divider\" />\n\n <rtsee-chat-members\n [chatInfo]=\"chatInfo\"\n [messenger]=\"messenger\"\n ></rtsee-chat-members>\n\n @if (chatInfo.myRole === RTSeeChatMemberRoles.ADMIN) {\n <hr class=\"rts-divider\" />\n <div class=\"rts-chat-info-danger-zone\">\n <button class=\"rts-btn rts-btn-danger rts-btn-block\"\n (click)=\"onDeleteChat()\"\n >\n Delete Chat\n </button>\n </div>\n }\n</div>\n" }]
|
|
1368
1439
|
}], propDecorators: { messenger: [{
|
|
1369
1440
|
type: Input
|
|
1370
1441
|
}], chatInfo: [{
|
|
@@ -1517,13 +1588,19 @@ class MessengerHeaderComponent {
|
|
|
1517
1588
|
constructor(defaultImagesService) {
|
|
1518
1589
|
this.defaultImagesService = defaultImagesService;
|
|
1519
1590
|
this.isDropdownOpen = false;
|
|
1520
|
-
this.
|
|
1591
|
+
this.imageExpandedToSquare = false;
|
|
1592
|
+
this.lastSquareToggleTime = 0;
|
|
1521
1593
|
this.RTSeeTextLabelKeys = RTSeeTextLabelKeys;
|
|
1522
1594
|
}
|
|
1523
1595
|
openProfile(id) {
|
|
1524
1596
|
if (this.messenger.ownProfileOpened) {
|
|
1525
1597
|
return;
|
|
1526
1598
|
}
|
|
1599
|
+
this.imageExpandedToSquare = false;
|
|
1600
|
+
if (this.messenger.myPeer && id === this.messenger.myPeer.clientId) {
|
|
1601
|
+
this.messenger.ownProfileOpened = true;
|
|
1602
|
+
this.messenger.openedProfile = this.messenger.myPeer;
|
|
1603
|
+
}
|
|
1527
1604
|
void this.messenger.openProfile(id);
|
|
1528
1605
|
}
|
|
1529
1606
|
toggleDropdown() {
|
|
@@ -1542,35 +1619,85 @@ class MessengerHeaderComponent {
|
|
|
1542
1619
|
onChatSaved(chat) {
|
|
1543
1620
|
this.messenger.closeManageChatInfo();
|
|
1544
1621
|
}
|
|
1622
|
+
onMyAvatarClick() {
|
|
1623
|
+
if (!this.messenger.ownProfileOpened) {
|
|
1624
|
+
if (this.messenger.myPeer) {
|
|
1625
|
+
this.openProfile(this.messenger.myPeer.clientId);
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
else if (this.imageExpandedToSquare) {
|
|
1629
|
+
if (Date.now() - this.lastSquareToggleTime > 300) {
|
|
1630
|
+
this.imageExpandedToSquare = false;
|
|
1631
|
+
this.lastSquareToggleTime = Date.now();
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
onMyAvatarDblClick() {
|
|
1636
|
+
if (this.messenger.ownProfileOpened) {
|
|
1637
|
+
if (Date.now() - this.lastSquareToggleTime > 300) {
|
|
1638
|
+
this.imageExpandedToSquare = !this.imageExpandedToSquare;
|
|
1639
|
+
this.lastSquareToggleTime = Date.now();
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1545
1643
|
onChatImageClick() {
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1644
|
+
const isExpanded = !!(this.messenger.openedProfile || this.messenger.openedChatDetails);
|
|
1645
|
+
if (!isExpanded) {
|
|
1646
|
+
// First click: open chat info (enters State 2: centered circle)
|
|
1647
|
+
this.imageExpandedToSquare = false;
|
|
1549
1648
|
this.messenger.openChatInfo();
|
|
1550
1649
|
}
|
|
1551
|
-
else if (
|
|
1552
|
-
//
|
|
1553
|
-
this.
|
|
1650
|
+
else if (this.imageExpandedToSquare) {
|
|
1651
|
+
// In square state: single click collapses back to circle
|
|
1652
|
+
if (Date.now() - this.lastSquareToggleTime > 300) {
|
|
1653
|
+
this.imageExpandedToSquare = false;
|
|
1654
|
+
this.lastSquareToggleTime = Date.now();
|
|
1655
|
+
}
|
|
1554
1656
|
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1657
|
+
}
|
|
1658
|
+
onChatImageDblClick() {
|
|
1659
|
+
const isExpanded = !!(this.messenger.openedProfile || this.messenger.openedChatDetails);
|
|
1660
|
+
if (isExpanded) {
|
|
1661
|
+
if (Date.now() - this.lastSquareToggleTime > 300) {
|
|
1662
|
+
this.imageExpandedToSquare = !this.imageExpandedToSquare;
|
|
1663
|
+
this.lastSquareToggleTime = Date.now();
|
|
1664
|
+
}
|
|
1558
1665
|
}
|
|
1559
1666
|
}
|
|
1560
1667
|
collapseInfo() {
|
|
1561
|
-
this.
|
|
1668
|
+
this.imageExpandedToSquare = false;
|
|
1562
1669
|
this.messenger.collapseInfo();
|
|
1563
1670
|
}
|
|
1564
1671
|
get isGroupOrChannelChat() {
|
|
1565
1672
|
return this.messenger.openedChat?.type === RTSeeChatTypes.GROUP ||
|
|
1566
1673
|
this.messenger.openedChat?.type === RTSeeChatTypes.CHANNEL;
|
|
1567
1674
|
}
|
|
1675
|
+
get headerTitle() {
|
|
1676
|
+
if (this.messenger.ownProfileOpened) {
|
|
1677
|
+
return this.messenger.myPeer?.name || 'My Profile';
|
|
1678
|
+
}
|
|
1679
|
+
if (this.messenger.openedProfile?.name) {
|
|
1680
|
+
return this.messenger.openedProfile.name;
|
|
1681
|
+
}
|
|
1682
|
+
if (this.messenger.openedChat?.name) {
|
|
1683
|
+
return this.messenger.openedChat.name;
|
|
1684
|
+
}
|
|
1685
|
+
return '';
|
|
1686
|
+
}
|
|
1687
|
+
get myProfileImageUrl() {
|
|
1688
|
+
return this.messenger?.myPeer?.imageUrl || this.defaultImagesService.PROFILE;
|
|
1689
|
+
}
|
|
1690
|
+
get chatImageUrl() {
|
|
1691
|
+
return this.messenger?.openedProfile?.imageUrl ||
|
|
1692
|
+
this.messenger?.openedChat?.imageUrl ||
|
|
1693
|
+
this.defaultImagesService.PROFILE;
|
|
1694
|
+
}
|
|
1568
1695
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MessengerHeaderComponent, deps: [{ token: DefaultImagesService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1569
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: MessengerHeaderComponent, isStandalone: true, selector: "rtsee-messenger-header", inputs: { messenger: "messenger" }, ngImport: i0, template: "<div class=\"rts-messenger-header\"\n [ngClass]=\"{\n 'rts-messenger-header-chat-opened': messenger.openedChat && !messenger.manageChatInfo,\n 'rts-chat-header-unread': messenger.openedChat && !messenger.unreadMessagesCount,\n 'rts-messenger-header-expanded': messenger.openedProfile || messenger.openedChatDetails || messenger.manageChatInfo,\n 'rts-messenger-header-create-chat-mode': messenger.manageChatInfo,\n 'rts-messenger-header-my-profile-mode': messenger.ownProfileOpened,\n 'rts-messenger-header-chat-info-mode': (messenger.openedProfile || messenger.openedChatDetails) && !messenger.ownProfileOpened && !messenger.manageChatInfo,\n 'rts-messenger-header-chat-image-expanded':
|
|
1696
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: MessengerHeaderComponent, isStandalone: true, selector: "rtsee-messenger-header", inputs: { messenger: "messenger" }, ngImport: i0, template: "<div class=\"rts-messenger-header\"\n [ngClass]=\"{\n 'rts-messenger-header-chat-opened': messenger.openedChat && !messenger.manageChatInfo,\n 'rts-chat-header-unread': messenger.openedChat && !messenger.unreadMessagesCount,\n 'rts-messenger-header-expanded': messenger.openedProfile || messenger.openedChatDetails || messenger.manageChatInfo,\n 'rts-messenger-header-create-chat-mode': messenger.manageChatInfo,\n 'rts-messenger-header-my-profile-mode': messenger.ownProfileOpened,\n 'rts-messenger-header-chat-info-mode': (messenger.openedProfile || messenger.openedChatDetails) && !messenger.ownProfileOpened && !messenger.manageChatInfo,\n 'rts-messenger-header-chat-image-expanded': imageExpandedToSquare,\n 'rts-messenger-header-image-square': imageExpandedToSquare\n }\"\n>\n <span class=\"rts-messenger-header-collapse-button\"\n (click)=\"messenger.manageChatInfo ? closeManageChatDialog() : collapseInfo()\"\n >\n <span class=\"pi pi-arrow-left\"></span>\n </span>\n <div class=\"rts-messenger-header-left\">\n @if (!messenger.openedChat && messenger.myPeer) {\n <div class=\"rts-messenger-header-avatar\"\n (click)=\"onMyAvatarClick()\"\n (dblclick)=\"onMyAvatarDblClick()\"\n >\n <div [style.background-image]=\"'url(' + myProfileImageUrl + ')'\"\n class=\"rts-messenger-header-avatar-image\">\n </div>\n </div>\n } @else if (messenger.openedChat) {\n <button class=\"rts-messenger-close-chat\"\n (click)=\"messenger.closeChat()\"\n >\n <span class=\"material-icons-outlined\">arrow_back</span>\n @if (messenger.unreadMessagesCount) {\n <span class=\"unread-count\">{{messenger.unreadMessagesCount}}</span>\n }\n </button>\n }\n </div>\n <div class=\"rts-messenger-header-right\">\n @if (messenger.openedChat) {\n <div class=\"rts-messenger-chat-image-container rts-messenger-header-avatar\"\n (click)=\"onChatImageClick()\"\n (dblclick)=\"onChatImageDblClick()\"\n >\n <div class=\"rts-messenger-chat-image rts-messenger-header-avatar-image\"\n [style.background-image]=\"'url(' + chatImageUrl + ')'\"\n ></div>\n </div>\n } @else {\n <div class=\"rts-messenger-header-actions\">\n <button class=\"rts-messenger-header-add-button\"\n (click)=\"toggleDropdown()\"\n >\n <span class=\"material-icons-outlined\">add</span>\n </button>\n\n @if (isDropdownOpen) {\n <div class=\"rts-messenger-header-dropdown-backdrop\"\n (click)=\"closeDropdown()\"\n ></div>\n <div class=\"rts-messenger-header-dropdown\">\n <button class=\"rts-messenger-header-dropdown-item\"\n (click)=\"openNewChatDialog()\"\n >\n <span class=\"material-icons-outlined rts-messenger-header-dropdown-icon\">chat_bubble_outline</span>\n <span>New Chat</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n <div class=\"rts-messenger-header-center\">\n @if (messenger.manageChatInfo) {\n <div class=\"rts-messenger-header-create-chat-container\">\n <rtsee-manage-chat-info [messenger]=\"messenger\"\n [chat]=\"messenger.manageChatInfo.chat || undefined\"\n (chatSaved)=\"onChatSaved($event)\"\n (cancelled)=\"closeManageChatDialog()\"\n ></rtsee-manage-chat-info>\n </div>\n } @else if (messenger.openedChat) {\n <div [ngClass]=\"{'rts-messenger-header-opened-chat-info-expanded': messenger.openedProfile || messenger.openedChatDetails}\"\n class=\"rts-messenger-header-opened-chat-info\">\n <div class=\"rts-messenger-profile-header-placeholder\">\n <p class=\"rts-messenger-header-chat-title\"\n >{{ headerTitle }}</p>\n </div>\n\n <div class=\"rts-messenger-profile-data-container\">\n @if (messenger.openedChatDetails && isGroupOrChannelChat && messenger.chatInfo) {\n <rtsee-chat-info [messenger]=\"messenger\"\n [chatInfo]=\"messenger.chatInfo\"\n (editChatInfo)=\"messenger.openManageChatInfo(messenger.openedChat)\"\n (chatDeleted)=\"messenger.closeChatInfo()\"\n ></rtsee-chat-info>\n } @else if (messenger.openedChatDetails || messenger.openedProfile) {\n <rtsee-profile [messenger]=\"messenger\"\n ></rtsee-profile>\n }\n </div>\n </div>\n } @else {\n @if (messenger.ownProfileOpened) {\n <div class=\"rts-messenger-header-opened-chat-info rts-messenger-header-opened-chat-info-expanded\">\n <div class=\"rts-messenger-profile-header-placeholder\">\n <p class=\"rts-messenger-header-chat-title\">{{ headerTitle }}</p>\n </div>\n <div class=\"rts-messenger-profile-data-container\">\n <rtsee-profile [messenger]=\"messenger\"\n [peer]=\"messenger.myPeer\"\n ></rtsee-profile>\n </div>\n </div>\n } @else {\n <p class=\"rts-messenger-header-title\"\n >{{messenger.labels[RTSeeTextLabelKeys.MESSANGER_TITLE]}}</p>\n <rtsee-profile [messenger]=\"messenger\"\n [peer]=\"messenger.myPeer\"\n ></rtsee-profile>\n }\n }\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ProfileComponent, selector: "rtsee-profile", inputs: ["messenger", "peer"] }, { kind: "component", type: ChatInfoComponent, selector: "rtsee-chat-info", inputs: ["messenger", "chatInfo"], outputs: ["editChatInfo", "chatDeleted"] }, { kind: "component", type: ManageChatInfoComponent, selector: "rtsee-manage-chat-info", inputs: ["messenger", "chat"], outputs: ["chatSaved", "cancelled"] }] }); }
|
|
1570
1697
|
}
|
|
1571
1698
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MessengerHeaderComponent, decorators: [{
|
|
1572
1699
|
type: Component,
|
|
1573
|
-
args: [{ selector: 'rtsee-messenger-header', imports: [NgClass, ProfileComponent, ChatInfoComponent, ManageChatInfoComponent], standalone: true, template: "<div class=\"rts-messenger-header\"\n [ngClass]=\"{\n 'rts-messenger-header-chat-opened': messenger.openedChat && !messenger.manageChatInfo,\n 'rts-chat-header-unread': messenger.openedChat && !messenger.unreadMessagesCount,\n 'rts-messenger-header-expanded': messenger.openedProfile || messenger.openedChatDetails || messenger.manageChatInfo,\n 'rts-messenger-header-create-chat-mode': messenger.manageChatInfo,\n 'rts-messenger-header-my-profile-mode': messenger.ownProfileOpened,\n 'rts-messenger-header-chat-info-mode': (messenger.openedProfile || messenger.openedChatDetails) && !messenger.ownProfileOpened && !messenger.manageChatInfo,\n 'rts-messenger-header-chat-image-expanded':
|
|
1700
|
+
args: [{ selector: 'rtsee-messenger-header', imports: [NgClass, ProfileComponent, ChatInfoComponent, ManageChatInfoComponent], standalone: true, template: "<div class=\"rts-messenger-header\"\n [ngClass]=\"{\n 'rts-messenger-header-chat-opened': messenger.openedChat && !messenger.manageChatInfo,\n 'rts-chat-header-unread': messenger.openedChat && !messenger.unreadMessagesCount,\n 'rts-messenger-header-expanded': messenger.openedProfile || messenger.openedChatDetails || messenger.manageChatInfo,\n 'rts-messenger-header-create-chat-mode': messenger.manageChatInfo,\n 'rts-messenger-header-my-profile-mode': messenger.ownProfileOpened,\n 'rts-messenger-header-chat-info-mode': (messenger.openedProfile || messenger.openedChatDetails) && !messenger.ownProfileOpened && !messenger.manageChatInfo,\n 'rts-messenger-header-chat-image-expanded': imageExpandedToSquare,\n 'rts-messenger-header-image-square': imageExpandedToSquare\n }\"\n>\n <span class=\"rts-messenger-header-collapse-button\"\n (click)=\"messenger.manageChatInfo ? closeManageChatDialog() : collapseInfo()\"\n >\n <span class=\"pi pi-arrow-left\"></span>\n </span>\n <div class=\"rts-messenger-header-left\">\n @if (!messenger.openedChat && messenger.myPeer) {\n <div class=\"rts-messenger-header-avatar\"\n (click)=\"onMyAvatarClick()\"\n (dblclick)=\"onMyAvatarDblClick()\"\n >\n <div [style.background-image]=\"'url(' + myProfileImageUrl + ')'\"\n class=\"rts-messenger-header-avatar-image\">\n </div>\n </div>\n } @else if (messenger.openedChat) {\n <button class=\"rts-messenger-close-chat\"\n (click)=\"messenger.closeChat()\"\n >\n <span class=\"material-icons-outlined\">arrow_back</span>\n @if (messenger.unreadMessagesCount) {\n <span class=\"unread-count\">{{messenger.unreadMessagesCount}}</span>\n }\n </button>\n }\n </div>\n <div class=\"rts-messenger-header-right\">\n @if (messenger.openedChat) {\n <div class=\"rts-messenger-chat-image-container rts-messenger-header-avatar\"\n (click)=\"onChatImageClick()\"\n (dblclick)=\"onChatImageDblClick()\"\n >\n <div class=\"rts-messenger-chat-image rts-messenger-header-avatar-image\"\n [style.background-image]=\"'url(' + chatImageUrl + ')'\"\n ></div>\n </div>\n } @else {\n <div class=\"rts-messenger-header-actions\">\n <button class=\"rts-messenger-header-add-button\"\n (click)=\"toggleDropdown()\"\n >\n <span class=\"material-icons-outlined\">add</span>\n </button>\n\n @if (isDropdownOpen) {\n <div class=\"rts-messenger-header-dropdown-backdrop\"\n (click)=\"closeDropdown()\"\n ></div>\n <div class=\"rts-messenger-header-dropdown\">\n <button class=\"rts-messenger-header-dropdown-item\"\n (click)=\"openNewChatDialog()\"\n >\n <span class=\"material-icons-outlined rts-messenger-header-dropdown-icon\">chat_bubble_outline</span>\n <span>New Chat</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n <div class=\"rts-messenger-header-center\">\n @if (messenger.manageChatInfo) {\n <div class=\"rts-messenger-header-create-chat-container\">\n <rtsee-manage-chat-info [messenger]=\"messenger\"\n [chat]=\"messenger.manageChatInfo.chat || undefined\"\n (chatSaved)=\"onChatSaved($event)\"\n (cancelled)=\"closeManageChatDialog()\"\n ></rtsee-manage-chat-info>\n </div>\n } @else if (messenger.openedChat) {\n <div [ngClass]=\"{'rts-messenger-header-opened-chat-info-expanded': messenger.openedProfile || messenger.openedChatDetails}\"\n class=\"rts-messenger-header-opened-chat-info\">\n <div class=\"rts-messenger-profile-header-placeholder\">\n <p class=\"rts-messenger-header-chat-title\"\n >{{ headerTitle }}</p>\n </div>\n\n <div class=\"rts-messenger-profile-data-container\">\n @if (messenger.openedChatDetails && isGroupOrChannelChat && messenger.chatInfo) {\n <rtsee-chat-info [messenger]=\"messenger\"\n [chatInfo]=\"messenger.chatInfo\"\n (editChatInfo)=\"messenger.openManageChatInfo(messenger.openedChat)\"\n (chatDeleted)=\"messenger.closeChatInfo()\"\n ></rtsee-chat-info>\n } @else if (messenger.openedChatDetails || messenger.openedProfile) {\n <rtsee-profile [messenger]=\"messenger\"\n ></rtsee-profile>\n }\n </div>\n </div>\n } @else {\n @if (messenger.ownProfileOpened) {\n <div class=\"rts-messenger-header-opened-chat-info rts-messenger-header-opened-chat-info-expanded\">\n <div class=\"rts-messenger-profile-header-placeholder\">\n <p class=\"rts-messenger-header-chat-title\">{{ headerTitle }}</p>\n </div>\n <div class=\"rts-messenger-profile-data-container\">\n <rtsee-profile [messenger]=\"messenger\"\n [peer]=\"messenger.myPeer\"\n ></rtsee-profile>\n </div>\n </div>\n } @else {\n <p class=\"rts-messenger-header-title\"\n >{{messenger.labels[RTSeeTextLabelKeys.MESSANGER_TITLE]}}</p>\n <rtsee-profile [messenger]=\"messenger\"\n [peer]=\"messenger.myPeer\"\n ></rtsee-profile>\n }\n }\n </div>\n</div>\n" }]
|
|
1574
1701
|
}], ctorParameters: () => [{ type: DefaultImagesService }], propDecorators: { messenger: [{
|
|
1575
1702
|
type: Input
|
|
1576
1703
|
}] } });
|
|
@@ -1806,11 +1933,11 @@ class RatingComponent {
|
|
|
1806
1933
|
this.init();
|
|
1807
1934
|
}
|
|
1808
1935
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RatingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1809
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RatingComponent, isStandalone: true, selector: "rtsee-rating", inputs: { config: "config", rate: "rate" }, ngImport: i0, template: "<div class=\"rtsee-rating\">\n @for (star of stars; track $index) {\n <span class=\"rtsee-rating-star\"\n [ngClass]=\"{\n 'rtsee-rating-star-full': star === 'full',\n 'rtsee-rating-star-half': star === 'half',\n 'rtsee-rating-star-empty': star === 'empty',\n 'rtsee-rating-star-clickable': mutable\n }\"\n (click)=\"onStarClicked($index)\"\n >\u2605</span>\n }\n @if (rateValue) {\n <span class=\"rtsee-rating-number\">{{ rateValue }}</span>\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
|
|
1936
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RatingComponent, isStandalone: true, selector: "rtsee-rating", inputs: { config: "config", rate: "rate" }, ngImport: i0, template: "<div class=\"rtsee-rating\">\n @for (star of stars; track $index) {\n <span class=\"rtsee-rating-star\"\n [ngClass]=\"{\n 'rtsee-rating-star-full': star === 'full',\n 'rtsee-rating-star-half': star === 'half',\n 'rtsee-rating-star-empty': star === 'empty',\n 'rtsee-rating-star-clickable': mutable\n }\"\n (click)=\"onStarClicked($index)\"\n >\u2605</span>\n }\n @if (rateValue) {\n <span class=\"rtsee-rating-number\">{{ rateValue | number:'1.1-1' }}</span>\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: DecimalPipe, name: "number" }] }); }
|
|
1810
1937
|
}
|
|
1811
1938
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RatingComponent, decorators: [{
|
|
1812
1939
|
type: Component,
|
|
1813
|
-
args: [{ selector: 'rtsee-rating', imports: [NgClass], template: "<div class=\"rtsee-rating\">\n @for (star of stars; track $index) {\n <span class=\"rtsee-rating-star\"\n [ngClass]=\"{\n 'rtsee-rating-star-full': star === 'full',\n 'rtsee-rating-star-half': star === 'half',\n 'rtsee-rating-star-empty': star === 'empty',\n 'rtsee-rating-star-clickable': mutable\n }\"\n (click)=\"onStarClicked($index)\"\n >\u2605</span>\n }\n @if (rateValue) {\n <span class=\"rtsee-rating-number\">{{ rateValue }}</span>\n }\n</div>\n" }]
|
|
1940
|
+
args: [{ selector: 'rtsee-rating', imports: [NgClass, DecimalPipe], template: "<div class=\"rtsee-rating\">\n @for (star of stars; track $index) {\n <span class=\"rtsee-rating-star\"\n [ngClass]=\"{\n 'rtsee-rating-star-full': star === 'full',\n 'rtsee-rating-star-half': star === 'half',\n 'rtsee-rating-star-empty': star === 'empty',\n 'rtsee-rating-star-clickable': mutable\n }\"\n (click)=\"onStarClicked($index)\"\n >\u2605</span>\n }\n @if (rateValue) {\n <span class=\"rtsee-rating-number\">{{ rateValue | number:'1.1-1' }}</span>\n }\n</div>\n" }]
|
|
1814
1941
|
}], propDecorators: { config: [{
|
|
1815
1942
|
type: Input
|
|
1816
1943
|
}], rate: [{
|
|
@@ -1819,26 +1946,109 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
1819
1946
|
|
|
1820
1947
|
class CallWidgetComponent {
|
|
1821
1948
|
ngOnInit() { }
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1949
|
+
get roomId() {
|
|
1950
|
+
return this.message?.data?.roomId || this.chat?.conferenceRoom?.id || null;
|
|
1951
|
+
}
|
|
1952
|
+
get callerName() {
|
|
1953
|
+
return this.message?.from?.name || 'A participant';
|
|
1954
|
+
}
|
|
1955
|
+
get isUserInCall() {
|
|
1956
|
+
const videoChat = this.messenger?.videoChat;
|
|
1957
|
+
const currentRoomId = videoChat?.roomId;
|
|
1958
|
+
return !!(videoChat?.isInActiveCall &&
|
|
1959
|
+
currentRoomId &&
|
|
1960
|
+
currentRoomId === this.roomId);
|
|
1961
|
+
}
|
|
1962
|
+
get presentMembers() {
|
|
1963
|
+
const membersMap = new Map();
|
|
1964
|
+
// 1. Caller who started the call
|
|
1965
|
+
if (this.message?.from) {
|
|
1966
|
+
const from = this.message.from;
|
|
1967
|
+
const id = from.id || from.clientId;
|
|
1968
|
+
const name = from.name || 'Caller';
|
|
1969
|
+
if (id) {
|
|
1970
|
+
membersMap.set(id, {
|
|
1971
|
+
id,
|
|
1972
|
+
name,
|
|
1973
|
+
imageUrl: from.imageUrl,
|
|
1974
|
+
initials: this.getInitials(name),
|
|
1975
|
+
});
|
|
1976
|
+
}
|
|
1828
1977
|
}
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1978
|
+
// 2. Members specified in call message data
|
|
1979
|
+
const callMembers = this.message?.data?.members;
|
|
1980
|
+
if (Array.isArray(callMembers)) {
|
|
1981
|
+
for (const m of callMembers) {
|
|
1982
|
+
const id = m.clientId || m.id;
|
|
1983
|
+
if (id && !membersMap.has(id)) {
|
|
1984
|
+
const name = m.name || id;
|
|
1985
|
+
membersMap.set(id, {
|
|
1986
|
+
id,
|
|
1987
|
+
name,
|
|
1988
|
+
imageUrl: m.imageUrl,
|
|
1989
|
+
initials: this.getInitials(name),
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
// 3. Live connected peers from WebRTC conference if active
|
|
1995
|
+
const videoChat = this.messenger?.videoChat;
|
|
1996
|
+
if (videoChat) {
|
|
1997
|
+
// Add current user if in active call for this room
|
|
1998
|
+
if (videoChat.isInActiveCall && videoChat.roomId === this.roomId && this.messenger.myPeer) {
|
|
1999
|
+
const myId = this.messenger.myPeer.clientId;
|
|
2000
|
+
const myName = this.messenger.myPeer.name || 'You';
|
|
2001
|
+
if (myId && !membersMap.has(myId)) {
|
|
2002
|
+
membersMap.set(myId, {
|
|
2003
|
+
id: myId,
|
|
2004
|
+
name: myName,
|
|
2005
|
+
imageUrl: this.messenger.myPeer.imageUrl,
|
|
2006
|
+
initials: this.getInitials(myName),
|
|
2007
|
+
});
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
// Add connected peers
|
|
2011
|
+
const conns = videoChat.rtcPeerConnections?.connections || [];
|
|
2012
|
+
for (const conn of conns) {
|
|
2013
|
+
const peerId = conn.clientId;
|
|
2014
|
+
if (peerId && !membersMap.has(peerId)) {
|
|
2015
|
+
const chatMember = this.chat?.getMemberById(peerId);
|
|
2016
|
+
const name = conn.name || chatMember?.name || peerId;
|
|
2017
|
+
const imageUrl = conn.imageUrl || chatMember?.imageUrl;
|
|
2018
|
+
membersMap.set(peerId, {
|
|
2019
|
+
id: peerId,
|
|
2020
|
+
name,
|
|
2021
|
+
imageUrl,
|
|
2022
|
+
initials: this.getInitials(name),
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
return Array.from(membersMap.values());
|
|
2028
|
+
}
|
|
2029
|
+
getInitials(name) {
|
|
2030
|
+
const trimmed = name.trim();
|
|
2031
|
+
if (!trimmed)
|
|
2032
|
+
return '?';
|
|
2033
|
+
const parts = trimmed.split(' ');
|
|
2034
|
+
if (parts.length >= 2 && parts[1]) {
|
|
2035
|
+
return (parts[0][0] + parts[1][0]).toUpperCase();
|
|
2036
|
+
}
|
|
2037
|
+
return trimmed.slice(0, 2).toUpperCase();
|
|
2038
|
+
}
|
|
2039
|
+
joinCall() {
|
|
2040
|
+
if (!this.messenger || !this.roomId) {
|
|
2041
|
+
console.log('Room Id unavailable or no messenger!');
|
|
1832
2042
|
return;
|
|
1833
2043
|
}
|
|
1834
|
-
void this.messenger.joinCall(roomId);
|
|
2044
|
+
void this.messenger.joinCall(this.roomId);
|
|
1835
2045
|
}
|
|
1836
2046
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: CallWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1837
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
2047
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: CallWidgetComponent, isStandalone: true, selector: "ngx-call-widget", inputs: { message: "message", messenger: "messenger", chat: "chat" }, ngImport: i0, template: "<div class=\"rts-call-widget\" [class.rts-call-widget-active]=\"isUserInCall\">\n <div class=\"rts-call-widget-header\">\n <div class=\"rts-call-widget-badge\">\n <span class=\"rts-call-live-dot\"></span>\n <span class=\"rts-call-badge-text\">{{ isUserInCall ? 'Connected' : 'Live Call' }}</span>\n </div>\n <span class=\"material-icons-outlined rts-call-widget-icon\">videocam</span>\n </div>\n\n <div class=\"rts-call-widget-content\">\n <p class=\"rts-call-widget-subtitle\">\n Started by <strong>{{ callerName }}</strong>\n </p>\n\n @if (presentMembers.length > 0) {\n <div class=\"rts-call-members-section\">\n <span class=\"rts-call-members-label\">In call ({{ presentMembers.length }}):</span>\n <div class=\"rts-call-avatar-stack\">\n @for (member of presentMembers; track member.id) {\n <div class=\"rts-call-avatar-item\" [title]=\"member.name\">\n @if (member.imageUrl) {\n <img [src]=\"member.imageUrl\" [alt]=\"member.name\" class=\"rts-call-avatar-img\" />\n } @else {\n <div class=\"rts-call-avatar-fallback\">\n {{ member.initials }}\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n\n <div class=\"rts-call-widget-actions\">\n @if (isUserInCall) {\n <button class=\"rts-call-action-btn rts-call-btn-connected\" disabled type=\"button\">\n <span class=\"material-icons-outlined\">check_circle</span>\n <span>In Call</span>\n </button>\n } @else {\n <button class=\"rts-call-action-btn rts-join-call-btn\"\n (click)=\"joinCall()\"\n type=\"button\"\n >\n <span class=\"material-icons-outlined\">call</span>\n <span>Join Call</span>\n </button>\n }\n </div>\n</div>\n", styles: [".rts-theme-dark{--rts-bg-primary: #0e0e10;--rts-bg-surface: #1a1a1e;--rts-bg-surface-elevated: #242428;--rts-bg-surface-hover: rgba(255, 255, 255, .05);--rts-bg-gradient: linear-gradient(135deg, #1b0533 0%, #0e0e10 60%, #0a1628 100%);--rts-text-primary: #e5e5e7;--rts-text-secondary: #8e8e93;--rts-text-muted: #6a6a7a;--rts-accent: #6c5ce7;--rts-accent-light: #a29bfe;--rts-accent-blue: #3b82f7;--rts-accent-highlight: #bf8bff;--rts-accent-highlight-dim: rgba(191, 139, 255, .15);--rts-border-dim: rgba(255, 255, 255, .05);--rts-border-med: rgba(255, 255, 255, .08);--rts-border-bright: rgba(255, 255, 255, .15);--rts-card-bg-start: rgba(28, 28, 40, .8);--rts-card-bg-end: rgba(20, 20, 30, .9);--rts-danger: #e84118;--rts-danger-bg: rgba(232, 65, 24, .05);--rts-danger-border: rgba(232, 65, 24, .1);--rts-success: #2ecc71;--rts-warning: #f9ca24;--rts-shadow-rgb: 0, 0, 0;--rts-glass-tint: rgba(255, 255, 255, .03)}.rts-theme-light{--rts-bg-primary: #f8f9fa;--rts-bg-surface: #ffffff;--rts-bg-surface-elevated: #f0f0f4;--rts-bg-surface-hover: rgba(0, 0, 0, .04);--rts-bg-gradient: linear-gradient(135deg, #f0ecff 0%, #f8f9fa 60%, #edf4ff 100%);--rts-text-primary: #1a1a2e;--rts-text-secondary: #555570;--rts-text-muted: #8888a0;--rts-accent: #6c5ce7;--rts-accent-light: #a29bfe;--rts-accent-blue: #3b82f7;--rts-accent-highlight: #bf8bff;--rts-accent-highlight-dim: rgba(191, 139, 255, .1);--rts-border-dim: rgba(0, 0, 0, .06);--rts-border-med: rgba(0, 0, 0, .1);--rts-border-bright: rgba(0, 0, 0, .15);--rts-card-bg-start: rgba(255, 255, 255, .95);--rts-card-bg-end: rgba(248, 248, 255, .98);--rts-danger: #e84118;--rts-danger-bg: rgba(232, 65, 24, .06);--rts-danger-border: rgba(232, 65, 24, .15);--rts-success: #2ecc71;--rts-warning: #f9ca24;--rts-shadow-rgb: 0, 0, 0;--rts-glass-tint: rgba(0, 0, 0, .02)}.rts-call-widget{min-width:250px;max-width:320px;background:linear-gradient(135deg,#ffffff12,#ffffff05);border:1px solid var(--rts-border-med, rgba(255, 255, 255, .12));-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);border-radius:14px;padding:14px;box-shadow:0 8px 32px #00000040;transition:transform .2s ease,box-shadow .2s ease,border-color .2s ease}.rts-call-widget:hover{border-color:#10b98159;box-shadow:0 10px 36px #10b9811f}.rts-call-widget.rts-call-widget-active{border-color:#10b98180;background:linear-gradient(135deg,#10b9811f,#ffffff05)}.rts-call-widget .rts-call-widget-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}.rts-call-widget .rts-call-widget-header .rts-call-widget-badge{display:inline-flex;align-items:center;gap:6px;padding:3px 8px;background:#10b98126;border:1px solid rgba(16,185,129,.3);border-radius:20px}.rts-call-widget .rts-call-widget-header .rts-call-widget-badge .rts-call-live-dot{width:7px;height:7px;border-radius:50%;background-color:#10b981;box-shadow:0 0 8px #10b981;animation:rts-call-pulse 1.8s infinite}.rts-call-widget .rts-call-widget-header .rts-call-widget-badge .rts-call-badge-text{font-size:11px;font-weight:700;color:#10b981;text-transform:uppercase;letter-spacing:.5px}.rts-call-widget .rts-call-widget-header .rts-call-widget-icon{font-size:18px;color:var(--rts-text-muted, rgba(255, 255, 255, .5))}.rts-call-widget .rts-call-widget-content{margin-bottom:12px}.rts-call-widget .rts-call-widget-content .rts-call-widget-subtitle{margin:0 0 8px;font-size:13px;color:var(--rts-text-secondary, rgba(255, 255, 255, .7))}.rts-call-widget .rts-call-widget-content .rts-call-widget-subtitle strong{color:var(--rts-text-primary, #fff)}.rts-call-widget .rts-call-widget-content .rts-call-members-section{display:flex;align-items:center;gap:8px;margin-top:6px}.rts-call-widget .rts-call-widget-content .rts-call-members-section .rts-call-members-label{font-size:11px;color:var(--rts-text-muted, rgba(255, 255, 255, .5));font-weight:500}.rts-call-widget .rts-call-widget-content .rts-call-members-section .rts-call-avatar-stack{display:flex;align-items:center}.rts-call-widget .rts-call-widget-content .rts-call-members-section .rts-call-avatar-stack .rts-call-avatar-item{width:26px;height:26px;border-radius:50%;border:2px solid var(--rts-bg-primary, #1e1e2d);margin-left:-6px;position:relative;overflow:hidden;transition:transform .2s ease,z-index .2s ease}.rts-call-widget .rts-call-widget-content .rts-call-members-section .rts-call-avatar-stack .rts-call-avatar-item:first-child{margin-left:0}.rts-call-widget .rts-call-widget-content .rts-call-members-section .rts-call-avatar-stack .rts-call-avatar-item:hover{transform:translateY(-2px) scale(1.15);z-index:10}.rts-call-widget .rts-call-widget-content .rts-call-members-section .rts-call-avatar-stack .rts-call-avatar-item .rts-call-avatar-img{width:100%;height:100%;object-fit:cover;border-radius:50%}.rts-call-widget .rts-call-widget-content .rts-call-members-section .rts-call-avatar-stack .rts-call-avatar-item .rts-call-avatar-fallback{width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#6c5ce7,#a29bfe);color:#fff;font-size:10px;font-weight:700}.rts-call-widget .rts-call-widget-actions .rts-call-action-btn{width:100%;display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:8px 14px;border-radius:10px;font-size:13px;font-weight:600;cursor:pointer;border:none;transition:all .2s ease}.rts-call-widget .rts-call-widget-actions .rts-call-action-btn .material-icons-outlined{font-size:16px}.rts-call-widget .rts-call-widget-actions .rts-join-call-btn{background:linear-gradient(135deg,#10b981,#059669);color:#fff;box-shadow:0 4px 14px #10b98159}.rts-call-widget .rts-call-widget-actions .rts-join-call-btn:hover{transform:translateY(-1px);box-shadow:0 6px 20px #10b98173}.rts-call-widget .rts-call-widget-actions .rts-join-call-btn:active{transform:translateY(0)}.rts-call-widget .rts-call-widget-actions .rts-call-btn-connected{background:#10b98126;border:1px solid rgba(16,185,129,.3);color:#10b981;cursor:default}@keyframes rts-call-pulse{0%{transform:scale(.95);box-shadow:0 0 #10b981b3}70%{transform:scale(1);box-shadow:0 0 0 6px #10b98100}to{transform:scale(.95);box-shadow:0 0 #10b98100}}\n"] }); }
|
|
1838
2048
|
}
|
|
1839
2049
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: CallWidgetComponent, decorators: [{
|
|
1840
2050
|
type: Component,
|
|
1841
|
-
args: [{ selector: 'ngx-call-widget',
|
|
2051
|
+
args: [{ selector: 'ngx-call-widget', standalone: true, template: "<div class=\"rts-call-widget\" [class.rts-call-widget-active]=\"isUserInCall\">\n <div class=\"rts-call-widget-header\">\n <div class=\"rts-call-widget-badge\">\n <span class=\"rts-call-live-dot\"></span>\n <span class=\"rts-call-badge-text\">{{ isUserInCall ? 'Connected' : 'Live Call' }}</span>\n </div>\n <span class=\"material-icons-outlined rts-call-widget-icon\">videocam</span>\n </div>\n\n <div class=\"rts-call-widget-content\">\n <p class=\"rts-call-widget-subtitle\">\n Started by <strong>{{ callerName }}</strong>\n </p>\n\n @if (presentMembers.length > 0) {\n <div class=\"rts-call-members-section\">\n <span class=\"rts-call-members-label\">In call ({{ presentMembers.length }}):</span>\n <div class=\"rts-call-avatar-stack\">\n @for (member of presentMembers; track member.id) {\n <div class=\"rts-call-avatar-item\" [title]=\"member.name\">\n @if (member.imageUrl) {\n <img [src]=\"member.imageUrl\" [alt]=\"member.name\" class=\"rts-call-avatar-img\" />\n } @else {\n <div class=\"rts-call-avatar-fallback\">\n {{ member.initials }}\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n\n <div class=\"rts-call-widget-actions\">\n @if (isUserInCall) {\n <button class=\"rts-call-action-btn rts-call-btn-connected\" disabled type=\"button\">\n <span class=\"material-icons-outlined\">check_circle</span>\n <span>In Call</span>\n </button>\n } @else {\n <button class=\"rts-call-action-btn rts-join-call-btn\"\n (click)=\"joinCall()\"\n type=\"button\"\n >\n <span class=\"material-icons-outlined\">call</span>\n <span>Join Call</span>\n </button>\n }\n </div>\n</div>\n", styles: [".rts-theme-dark{--rts-bg-primary: #0e0e10;--rts-bg-surface: #1a1a1e;--rts-bg-surface-elevated: #242428;--rts-bg-surface-hover: rgba(255, 255, 255, .05);--rts-bg-gradient: linear-gradient(135deg, #1b0533 0%, #0e0e10 60%, #0a1628 100%);--rts-text-primary: #e5e5e7;--rts-text-secondary: #8e8e93;--rts-text-muted: #6a6a7a;--rts-accent: #6c5ce7;--rts-accent-light: #a29bfe;--rts-accent-blue: #3b82f7;--rts-accent-highlight: #bf8bff;--rts-accent-highlight-dim: rgba(191, 139, 255, .15);--rts-border-dim: rgba(255, 255, 255, .05);--rts-border-med: rgba(255, 255, 255, .08);--rts-border-bright: rgba(255, 255, 255, .15);--rts-card-bg-start: rgba(28, 28, 40, .8);--rts-card-bg-end: rgba(20, 20, 30, .9);--rts-danger: #e84118;--rts-danger-bg: rgba(232, 65, 24, .05);--rts-danger-border: rgba(232, 65, 24, .1);--rts-success: #2ecc71;--rts-warning: #f9ca24;--rts-shadow-rgb: 0, 0, 0;--rts-glass-tint: rgba(255, 255, 255, .03)}.rts-theme-light{--rts-bg-primary: #f8f9fa;--rts-bg-surface: #ffffff;--rts-bg-surface-elevated: #f0f0f4;--rts-bg-surface-hover: rgba(0, 0, 0, .04);--rts-bg-gradient: linear-gradient(135deg, #f0ecff 0%, #f8f9fa 60%, #edf4ff 100%);--rts-text-primary: #1a1a2e;--rts-text-secondary: #555570;--rts-text-muted: #8888a0;--rts-accent: #6c5ce7;--rts-accent-light: #a29bfe;--rts-accent-blue: #3b82f7;--rts-accent-highlight: #bf8bff;--rts-accent-highlight-dim: rgba(191, 139, 255, .1);--rts-border-dim: rgba(0, 0, 0, .06);--rts-border-med: rgba(0, 0, 0, .1);--rts-border-bright: rgba(0, 0, 0, .15);--rts-card-bg-start: rgba(255, 255, 255, .95);--rts-card-bg-end: rgba(248, 248, 255, .98);--rts-danger: #e84118;--rts-danger-bg: rgba(232, 65, 24, .06);--rts-danger-border: rgba(232, 65, 24, .15);--rts-success: #2ecc71;--rts-warning: #f9ca24;--rts-shadow-rgb: 0, 0, 0;--rts-glass-tint: rgba(0, 0, 0, .02)}.rts-call-widget{min-width:250px;max-width:320px;background:linear-gradient(135deg,#ffffff12,#ffffff05);border:1px solid var(--rts-border-med, rgba(255, 255, 255, .12));-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);border-radius:14px;padding:14px;box-shadow:0 8px 32px #00000040;transition:transform .2s ease,box-shadow .2s ease,border-color .2s ease}.rts-call-widget:hover{border-color:#10b98159;box-shadow:0 10px 36px #10b9811f}.rts-call-widget.rts-call-widget-active{border-color:#10b98180;background:linear-gradient(135deg,#10b9811f,#ffffff05)}.rts-call-widget .rts-call-widget-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}.rts-call-widget .rts-call-widget-header .rts-call-widget-badge{display:inline-flex;align-items:center;gap:6px;padding:3px 8px;background:#10b98126;border:1px solid rgba(16,185,129,.3);border-radius:20px}.rts-call-widget .rts-call-widget-header .rts-call-widget-badge .rts-call-live-dot{width:7px;height:7px;border-radius:50%;background-color:#10b981;box-shadow:0 0 8px #10b981;animation:rts-call-pulse 1.8s infinite}.rts-call-widget .rts-call-widget-header .rts-call-widget-badge .rts-call-badge-text{font-size:11px;font-weight:700;color:#10b981;text-transform:uppercase;letter-spacing:.5px}.rts-call-widget .rts-call-widget-header .rts-call-widget-icon{font-size:18px;color:var(--rts-text-muted, rgba(255, 255, 255, .5))}.rts-call-widget .rts-call-widget-content{margin-bottom:12px}.rts-call-widget .rts-call-widget-content .rts-call-widget-subtitle{margin:0 0 8px;font-size:13px;color:var(--rts-text-secondary, rgba(255, 255, 255, .7))}.rts-call-widget .rts-call-widget-content .rts-call-widget-subtitle strong{color:var(--rts-text-primary, #fff)}.rts-call-widget .rts-call-widget-content .rts-call-members-section{display:flex;align-items:center;gap:8px;margin-top:6px}.rts-call-widget .rts-call-widget-content .rts-call-members-section .rts-call-members-label{font-size:11px;color:var(--rts-text-muted, rgba(255, 255, 255, .5));font-weight:500}.rts-call-widget .rts-call-widget-content .rts-call-members-section .rts-call-avatar-stack{display:flex;align-items:center}.rts-call-widget .rts-call-widget-content .rts-call-members-section .rts-call-avatar-stack .rts-call-avatar-item{width:26px;height:26px;border-radius:50%;border:2px solid var(--rts-bg-primary, #1e1e2d);margin-left:-6px;position:relative;overflow:hidden;transition:transform .2s ease,z-index .2s ease}.rts-call-widget .rts-call-widget-content .rts-call-members-section .rts-call-avatar-stack .rts-call-avatar-item:first-child{margin-left:0}.rts-call-widget .rts-call-widget-content .rts-call-members-section .rts-call-avatar-stack .rts-call-avatar-item:hover{transform:translateY(-2px) scale(1.15);z-index:10}.rts-call-widget .rts-call-widget-content .rts-call-members-section .rts-call-avatar-stack .rts-call-avatar-item .rts-call-avatar-img{width:100%;height:100%;object-fit:cover;border-radius:50%}.rts-call-widget .rts-call-widget-content .rts-call-members-section .rts-call-avatar-stack .rts-call-avatar-item .rts-call-avatar-fallback{width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#6c5ce7,#a29bfe);color:#fff;font-size:10px;font-weight:700}.rts-call-widget .rts-call-widget-actions .rts-call-action-btn{width:100%;display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:8px 14px;border-radius:10px;font-size:13px;font-weight:600;cursor:pointer;border:none;transition:all .2s ease}.rts-call-widget .rts-call-widget-actions .rts-call-action-btn .material-icons-outlined{font-size:16px}.rts-call-widget .rts-call-widget-actions .rts-join-call-btn{background:linear-gradient(135deg,#10b981,#059669);color:#fff;box-shadow:0 4px 14px #10b98159}.rts-call-widget .rts-call-widget-actions .rts-join-call-btn:hover{transform:translateY(-1px);box-shadow:0 6px 20px #10b98173}.rts-call-widget .rts-call-widget-actions .rts-join-call-btn:active{transform:translateY(0)}.rts-call-widget .rts-call-widget-actions .rts-call-btn-connected{background:#10b98126;border:1px solid rgba(16,185,129,.3);color:#10b981;cursor:default}@keyframes rts-call-pulse{0%{transform:scale(.95);box-shadow:0 0 #10b981b3}70%{transform:scale(1);box-shadow:0 0 0 6px #10b98100}to{transform:scale(.95);box-shadow:0 0 #10b98100}}\n"] }]
|
|
1842
2052
|
}], propDecorators: { message: [{
|
|
1843
2053
|
type: Input
|
|
1844
2054
|
}], messenger: [{
|
|
@@ -1890,6 +2100,8 @@ class MediaWidgetComponent {
|
|
|
1890
2100
|
this.hiddenCount = 0;
|
|
1891
2101
|
this.showAll = false;
|
|
1892
2102
|
this.downloadingUrl = null;
|
|
2103
|
+
this.loadedImageUrls = new Set();
|
|
2104
|
+
this.erroredImageUrls = new Set();
|
|
1893
2105
|
this.MAX_GRID = 4;
|
|
1894
2106
|
}
|
|
1895
2107
|
ngOnInit() {
|
|
@@ -1906,6 +2118,22 @@ class MediaWidgetComponent {
|
|
|
1906
2118
|
previewUrl(img) {
|
|
1907
2119
|
return img.smallVersionUrl || img.url;
|
|
1908
2120
|
}
|
|
2121
|
+
isImageLoaded(img) {
|
|
2122
|
+
const url = this.previewUrl(img);
|
|
2123
|
+
return this.loadedImageUrls.has(url);
|
|
2124
|
+
}
|
|
2125
|
+
isImageError(img) {
|
|
2126
|
+
const url = this.previewUrl(img);
|
|
2127
|
+
return this.erroredImageUrls.has(url);
|
|
2128
|
+
}
|
|
2129
|
+
onImageLoad(img) {
|
|
2130
|
+
const url = this.previewUrl(img);
|
|
2131
|
+
this.loadedImageUrls.add(url);
|
|
2132
|
+
}
|
|
2133
|
+
onImageError(img) {
|
|
2134
|
+
const url = this.previewUrl(img);
|
|
2135
|
+
this.erroredImageUrls.add(url);
|
|
2136
|
+
}
|
|
1909
2137
|
async downloadOriginal(event, img) {
|
|
1910
2138
|
event.stopPropagation();
|
|
1911
2139
|
event.preventDefault();
|
|
@@ -2012,11 +2240,11 @@ class MediaWidgetComponent {
|
|
|
2012
2240
|
}
|
|
2013
2241
|
}
|
|
2014
2242
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MediaWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2015
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: MediaWidgetComponent, isStandalone: true, selector: "rtsee-media-widget", inputs: { message: "message", messenger: "messenger", chat: "chat", showCost: "showCost" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"rts-media-widget\">\n <!-- Video player -->\n @if (message.video) {\n <div class=\"rts-media-video\">\n <video\n controls\n playsinline\n preload=\"metadata\"\n [src]=\"message.video.url\"\n class=\"rts-video-player\"\n >\n Your browser does not support video playback.\n </video>\n </div>\n }\n\n <!-- Image grid -->\n @if (hasImages) {\n <div class=\"rts-media-images\" [ngClass]=\"gridClass\">\n @for (img of visibleImages; track img.id || img.url) {\n <div class=\"rts-media-image-wrapper\">\n <img [src]=\"previewUrl(img)\" [alt]=\"img.name\" class=\"rts-media-image\" loading=\"lazy\" />\n <button\n type=\"button\"\n class=\"rts-media-btn-download\"\n [class.rts-downloading]=\"downloadingUrl === img.url\"\n title=\"Download original image\"\n aria-label=\"Download original image\"\n (click)=\"downloadOriginal($event, img)\"\n >\n <i class=\"pi\" [ngClass]=\"downloadingUrl === img.url ? 'pi-spin pi-spinner' : 'pi-download'\"></i>\n </button>\n <div class=\"rts-media-image-actions\">\n <button\n type=\"button\"\n class=\"rts-media-btn-paint\"\n title=\"Send image to Illustrator (Review & Iterate)\"\n (click)=\"sendToIllustrator($event, img)\"\n >\n <i class=\"pi pi-palette\"></i>\n <span>+ paint</span>\n </button>\n <button\n type=\"button\"\n class=\"rts-media-btn-context\"\n title=\"Add image to Illustrator context references\"\n (click)=\"addToContext($event, img)\"\n >\n <i class=\"pi pi-plus\"></i>\n <span>+ context</span>\n </button>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Show more / less toggle -->\n @if (hiddenCount > 0 || showAll) {\n <button class=\"rts-media-toggle\" (click)=\"toggleShowAll()\">\n {{ showAll ? 'Show less' : 'Show ' + hiddenCount + ' more' }}\n </button>\n }\n\n <!-- Cost display (optional, disabled when nested inside agentic widget) -->\n @if (showCost && message.cost) {\n <div class=\"rts-media-cost\">\n <rtsee-cost-display [cost]=\"message.cost\"></rtsee-cost-display>\n </div>\n }\n</div>\n", styles: [".rts-media-widget{max-width:100%}.rts-media-video{border-radius:8px;overflow:hidden;margin-bottom:6px}.rts-video-player{width:100%;max-height:360px;border-radius:8px;background:#000;display:block}.rts-media-images{display:grid;gap:4px;border-radius:8px;overflow:hidden;margin-bottom:4px}.rts-media-grid-1{grid-template-columns:1fr}.rts-media-grid-2{grid-template-columns:1fr 1fr}.rts-media-grid-4{grid-template-columns:1fr 1fr;grid-template-rows:auto auto}.rts-media-image-wrapper{position:relative;overflow:hidden;border-radius:6px;background:var(--rts-bg-secondary, #1e1e2e)}.rts-media-image-actions{position:absolute;top:8px;right:8px;z-index:5;display:inline-flex;align-items:center;gap:6px}.rts-media-btn-paint{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#e9d5ff;border:1px solid rgba(168,85,247,.45);border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-paint i{font-size:11px;color:#c084fc}.rts-media-btn-paint span{line-height:1}.rts-media-btn-paint:hover{background:#9333eae6;color:#fff;border-color:#a855f7;transform:translateY(-1px) scale(1.04);box-shadow:0 4px 12px #a855f773}.rts-media-btn-paint:hover i{color:#fff}.rts-media-btn-paint:active{transform:translateY(0) scale(.98)}.rts-media-btn-context{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#bae6fd;border:1px solid rgba(56,189,248,.45);border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-context i{font-size:11px;color:#38bdf8}.rts-media-btn-context span{line-height:1}.rts-media-btn-context:hover{background:#0ea5e9e6;color:#fff;border-color:#38bdf8;transform:translateY(-1px) scale(1.04);box-shadow:0 4px 12px #38bdf873}.rts-media-btn-context:hover i{color:#fff}.rts-media-btn-context:active{transform:translateY(0) scale(.98)}.rts-media-btn-download{position:absolute;left:8px;bottom:8px;z-index:5;width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;padding:0;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#d1fae5;border:1px solid rgba(52,211,153,.45);border-radius:50%;cursor:pointer;opacity:.85;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-download i{font-size:12px;color:#34d399}.rts-media-btn-download:hover{opacity:1;background:#10b981e6;border-color:#34d399;transform:translateY(-1px) scale(1.08);box-shadow:0 4px 12px #34d39973}.rts-media-btn-download:hover i{color:#fff}.rts-media-btn-download:active{transform:translateY(0) scale(.96)}.rts-media-btn-download.rts-downloading{cursor:progress;opacity:1}.rts-media-image{width:100%;height:auto;max-height:280px;object-fit:cover;display:block;transition:transform .2s ease;cursor:pointer}.rts-media-image:hover{transform:scale(1.02)}.rts-media-grid-1 .rts-media-image{max-height:400px;object-fit:contain}.rts-media-toggle{display:block;width:100%;padding:6px 0;border:none;background:none;color:var(--rts-agent-accent, #8b5cf6);font-size:12px;font-weight:500;cursor:pointer;text-align:center;transition:opacity .15s ease}.rts-media-toggle:hover{opacity:.8}.rts-media-cost{display:flex;justify-content:flex-end;padding-top:4px}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: CostDisplayComponent, selector: "rtsee-cost-display", inputs: ["cost"] }] }); }
|
|
2243
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: MediaWidgetComponent, isStandalone: true, selector: "rtsee-media-widget", inputs: { message: "message", messenger: "messenger", chat: "chat", showCost: "showCost" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"rts-media-widget\">\n <!-- Video player -->\n @if (message.video) {\n <div class=\"rts-media-video\">\n <video\n controls\n playsinline\n preload=\"metadata\"\n [src]=\"message.video.url\"\n class=\"rts-video-player\"\n >\n Your browser does not support video playback.\n </video>\n </div>\n }\n\n <!-- Image grid -->\n @if (hasImages) {\n <div class=\"rts-media-images\" [ngClass]=\"gridClass\">\n @for (img of visibleImages; track img.id || img.url) {\n <div class=\"rts-media-image-wrapper\" [class.rts-image-loaded]=\"isImageLoaded(img)\">\n <!-- Media Placeholder (preview before image loads) -->\n @if (!isImageLoaded(img)) {\n <div class=\"rts-media-placeholder\" [class.rts-placeholder-error]=\"isImageError(img)\">\n <div class=\"rts-media-placeholder-shimmer\"></div>\n <div class=\"rts-media-placeholder-content\">\n @if (isImageError(img)) {\n <span class=\"material-icons-outlined rts-media-placeholder-icon\">broken_image</span>\n <span class=\"rts-media-placeholder-text\">Failed to load</span>\n } @else {\n <div class=\"rts-media-placeholder-icon-wrap\">\n <span class=\"material-icons-outlined rts-media-placeholder-icon\">image</span>\n </div>\n <span class=\"rts-media-placeholder-text\">{{ img.name || 'Loading media preview...' }}</span>\n }\n </div>\n </div>\n }\n\n <img\n [src]=\"previewUrl(img)\"\n [alt]=\"img.name\"\n class=\"rts-media-image\"\n [class.rts-media-image-loaded]=\"isImageLoaded(img)\"\n (load)=\"onImageLoad(img)\"\n (error)=\"onImageError(img)\"\n loading=\"lazy\"\n />\n\n @if (isImageLoaded(img)) {\n <button\n type=\"button\"\n class=\"rts-media-btn-download\"\n [class.rts-downloading]=\"downloadingUrl === img.url\"\n title=\"Download original image\"\n aria-label=\"Download original image\"\n (click)=\"downloadOriginal($event, img)\"\n >\n <i class=\"pi\" [ngClass]=\"downloadingUrl === img.url ? 'pi-spin pi-spinner' : 'pi-download'\"></i>\n </button>\n <div class=\"rts-media-image-actions\">\n <button\n type=\"button\"\n class=\"rts-media-btn-paint\"\n title=\"Send image to Illustrator (Review & Iterate)\"\n (click)=\"sendToIllustrator($event, img)\"\n >\n <i class=\"pi pi-palette\"></i>\n <span>+ paint</span>\n </button>\n <button\n type=\"button\"\n class=\"rts-media-btn-context\"\n title=\"Add image to Illustrator context references\"\n (click)=\"addToContext($event, img)\"\n >\n <i class=\"pi pi-plus\"></i>\n <span>+ context</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Show more / less toggle -->\n @if (hiddenCount > 0 || showAll) {\n <button class=\"rts-media-toggle\" (click)=\"toggleShowAll()\">\n {{ showAll ? 'Show less' : 'Show ' + hiddenCount + ' more' }}\n </button>\n }\n\n <!-- Cost display (optional, disabled when nested inside agentic widget) -->\n @if (showCost && message.cost) {\n <div class=\"rts-media-cost\">\n <rtsee-cost-display [cost]=\"message.cost\"></rtsee-cost-display>\n </div>\n }\n</div>\n", styles: [".rts-media-widget{max-width:100%}.rts-media-video{border-radius:8px;overflow:hidden;margin-bottom:6px;aspect-ratio:16/9;background:#000}.rts-video-player{width:100%;height:100%;max-height:360px;border-radius:8px;background:#000;display:block}.rts-media-images{display:grid;gap:4px;border-radius:8px;overflow:hidden;margin-bottom:4px}.rts-media-grid-1{grid-template-columns:1fr}.rts-media-grid-1 .rts-media-image-wrapper{min-height:220px;aspect-ratio:16/10}.rts-media-grid-1 .rts-media-image-wrapper.rts-image-loaded{min-height:0;aspect-ratio:auto}.rts-media-grid-2{grid-template-columns:1fr 1fr}.rts-media-grid-2 .rts-media-image-wrapper{aspect-ratio:1/1;min-height:120px}.rts-media-grid-4{grid-template-columns:1fr 1fr;grid-template-rows:auto auto}.rts-media-grid-4 .rts-media-image-wrapper{aspect-ratio:1/1;min-height:120px}.rts-media-image-wrapper{position:relative;overflow:hidden;border-radius:6px;background:var(--rts-bg-secondary, #1e1e2e);display:flex;align-items:center;justify-content:center}.rts-media-placeholder{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#1a1a2af5,#12121cfa);border:1px solid rgba(255,255,255,.06);border-radius:6px;z-index:2;overflow:hidden;pointer-events:none}.rts-media-placeholder .rts-media-placeholder-shimmer{position:absolute;top:0;left:-150%;width:150%;height:100%;background:linear-gradient(90deg,transparent 0%,rgba(255,255,255,.03) 30%,rgba(168,85,247,.12) 50%,rgba(255,255,255,.03) 70%,transparent 100%);animation:rts-media-shimmer 2.2s infinite ease-in-out}.rts-media-placeholder .rts-media-placeholder-content{position:relative;z-index:3;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:12px;text-align:center;max-width:90%}.rts-media-placeholder .rts-media-placeholder-icon-wrap{width:42px;height:42px;border-radius:50%;background:#6c5ce726;border:1px solid rgba(168,85,247,.35);display:flex;align-items:center;justify-content:center;animation:rts-media-pulse 2s infinite ease-in-out;box-shadow:0 4px 16px #6c5ce733}.rts-media-placeholder .rts-media-placeholder-icon{font-size:22px;color:var(--rts-accent-light, #c084fc)}.rts-media-placeholder .rts-media-placeholder-text{font-size:11px;font-weight:500;color:var(--rts-text-muted, #94a3b8);max-width:180px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.2}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-icon-wrap{background:#ef444426;border-color:#ef444459;animation:none;box-shadow:none}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-icon{color:#ef4444}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-text{color:#f87171}@keyframes rts-media-shimmer{0%{transform:translate(0)}to{transform:translate(200%)}}@keyframes rts-media-pulse{0%,to{transform:scale(1);opacity:.85}50%{transform:scale(1.08);opacity:1;box-shadow:0 0 16px #a855f766}}.rts-media-image-actions{position:absolute;top:8px;right:8px;z-index:5;display:inline-flex;align-items:center;gap:6px}.rts-media-btn-paint{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#e9d5ff;border:1px solid rgba(168,85,247,.45);border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-paint i{font-size:11px;color:#c084fc}.rts-media-btn-paint span{line-height:1}.rts-media-btn-paint:hover{background:#9333eae6;color:#fff;border-color:#a855f7;transform:translateY(-1px) scale(1.04);box-shadow:0 4px 12px #a855f773}.rts-media-btn-paint:hover i{color:#fff}.rts-media-btn-paint:active{transform:translateY(0) scale(.98)}.rts-media-btn-context{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#bae6fd;border:1px solid rgba(56,189,248,.45);border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-context i{font-size:11px;color:#38bdf8}.rts-media-btn-context span{line-height:1}.rts-media-btn-context:hover{background:#0ea5e9e6;color:#fff;border-color:#38bdf8;transform:translateY(-1px) scale(1.04);box-shadow:0 4px 12px #38bdf873}.rts-media-btn-context:hover i{color:#fff}.rts-media-btn-context:active{transform:translateY(0) scale(.98)}.rts-media-btn-download{position:absolute;left:8px;bottom:8px;z-index:5;width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;padding:0;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#d1fae5;border:1px solid rgba(52,211,153,.45);border-radius:50%;cursor:pointer;opacity:.85;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-download i{font-size:12px;color:#34d399}.rts-media-btn-download:hover{opacity:1;background:#10b981e6;border-color:#34d399;transform:translateY(-1px) scale(1.08);box-shadow:0 4px 12px #34d39973}.rts-media-btn-download:hover i{color:#fff}.rts-media-btn-download:active{transform:translateY(0) scale(.96)}.rts-media-btn-download.rts-downloading{cursor:progress;opacity:1}.rts-media-image{width:100%;height:100%;max-height:280px;object-fit:cover;display:block;opacity:0;transition:opacity .25s ease-out,transform .2s ease;cursor:pointer}.rts-media-image.rts-media-image-loaded{opacity:1}.rts-media-image:hover{transform:scale(1.02)}.rts-media-grid-1 .rts-media-image{height:auto;max-height:400px;object-fit:contain}.rts-media-toggle{display:block;width:100%;padding:6px 0;border:none;background:none;color:var(--rts-agent-accent, #8b5cf6);font-size:12px;font-weight:500;cursor:pointer;text-align:center;transition:opacity .15s ease}.rts-media-toggle:hover{opacity:.8}.rts-media-cost{display:flex;justify-content:flex-end;padding-top:4px}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: CostDisplayComponent, selector: "rtsee-cost-display", inputs: ["cost"] }] }); }
|
|
2016
2244
|
}
|
|
2017
2245
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MediaWidgetComponent, decorators: [{
|
|
2018
2246
|
type: Component,
|
|
2019
|
-
args: [{ selector: 'rtsee-media-widget', standalone: true, imports: [NgClass, CostDisplayComponent], template: "<div class=\"rts-media-widget\">\n <!-- Video player -->\n @if (message.video) {\n <div class=\"rts-media-video\">\n <video\n controls\n playsinline\n preload=\"metadata\"\n [src]=\"message.video.url\"\n class=\"rts-video-player\"\n >\n Your browser does not support video playback.\n </video>\n </div>\n }\n\n <!-- Image grid -->\n @if (hasImages) {\n <div class=\"rts-media-images\" [ngClass]=\"gridClass\">\n @for (img of visibleImages; track img.id || img.url) {\n <div class=\"rts-media-image-wrapper\">\n <img [src]=\"previewUrl(img)\"
|
|
2247
|
+
args: [{ selector: 'rtsee-media-widget', standalone: true, imports: [NgClass, CostDisplayComponent], template: "<div class=\"rts-media-widget\">\n <!-- Video player -->\n @if (message.video) {\n <div class=\"rts-media-video\">\n <video\n controls\n playsinline\n preload=\"metadata\"\n [src]=\"message.video.url\"\n class=\"rts-video-player\"\n >\n Your browser does not support video playback.\n </video>\n </div>\n }\n\n <!-- Image grid -->\n @if (hasImages) {\n <div class=\"rts-media-images\" [ngClass]=\"gridClass\">\n @for (img of visibleImages; track img.id || img.url) {\n <div class=\"rts-media-image-wrapper\" [class.rts-image-loaded]=\"isImageLoaded(img)\">\n <!-- Media Placeholder (preview before image loads) -->\n @if (!isImageLoaded(img)) {\n <div class=\"rts-media-placeholder\" [class.rts-placeholder-error]=\"isImageError(img)\">\n <div class=\"rts-media-placeholder-shimmer\"></div>\n <div class=\"rts-media-placeholder-content\">\n @if (isImageError(img)) {\n <span class=\"material-icons-outlined rts-media-placeholder-icon\">broken_image</span>\n <span class=\"rts-media-placeholder-text\">Failed to load</span>\n } @else {\n <div class=\"rts-media-placeholder-icon-wrap\">\n <span class=\"material-icons-outlined rts-media-placeholder-icon\">image</span>\n </div>\n <span class=\"rts-media-placeholder-text\">{{ img.name || 'Loading media preview...' }}</span>\n }\n </div>\n </div>\n }\n\n <img\n [src]=\"previewUrl(img)\"\n [alt]=\"img.name\"\n class=\"rts-media-image\"\n [class.rts-media-image-loaded]=\"isImageLoaded(img)\"\n (load)=\"onImageLoad(img)\"\n (error)=\"onImageError(img)\"\n loading=\"lazy\"\n />\n\n @if (isImageLoaded(img)) {\n <button\n type=\"button\"\n class=\"rts-media-btn-download\"\n [class.rts-downloading]=\"downloadingUrl === img.url\"\n title=\"Download original image\"\n aria-label=\"Download original image\"\n (click)=\"downloadOriginal($event, img)\"\n >\n <i class=\"pi\" [ngClass]=\"downloadingUrl === img.url ? 'pi-spin pi-spinner' : 'pi-download'\"></i>\n </button>\n <div class=\"rts-media-image-actions\">\n <button\n type=\"button\"\n class=\"rts-media-btn-paint\"\n title=\"Send image to Illustrator (Review & Iterate)\"\n (click)=\"sendToIllustrator($event, img)\"\n >\n <i class=\"pi pi-palette\"></i>\n <span>+ paint</span>\n </button>\n <button\n type=\"button\"\n class=\"rts-media-btn-context\"\n title=\"Add image to Illustrator context references\"\n (click)=\"addToContext($event, img)\"\n >\n <i class=\"pi pi-plus\"></i>\n <span>+ context</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Show more / less toggle -->\n @if (hiddenCount > 0 || showAll) {\n <button class=\"rts-media-toggle\" (click)=\"toggleShowAll()\">\n {{ showAll ? 'Show less' : 'Show ' + hiddenCount + ' more' }}\n </button>\n }\n\n <!-- Cost display (optional, disabled when nested inside agentic widget) -->\n @if (showCost && message.cost) {\n <div class=\"rts-media-cost\">\n <rtsee-cost-display [cost]=\"message.cost\"></rtsee-cost-display>\n </div>\n }\n</div>\n", styles: [".rts-media-widget{max-width:100%}.rts-media-video{border-radius:8px;overflow:hidden;margin-bottom:6px;aspect-ratio:16/9;background:#000}.rts-video-player{width:100%;height:100%;max-height:360px;border-radius:8px;background:#000;display:block}.rts-media-images{display:grid;gap:4px;border-radius:8px;overflow:hidden;margin-bottom:4px}.rts-media-grid-1{grid-template-columns:1fr}.rts-media-grid-1 .rts-media-image-wrapper{min-height:220px;aspect-ratio:16/10}.rts-media-grid-1 .rts-media-image-wrapper.rts-image-loaded{min-height:0;aspect-ratio:auto}.rts-media-grid-2{grid-template-columns:1fr 1fr}.rts-media-grid-2 .rts-media-image-wrapper{aspect-ratio:1/1;min-height:120px}.rts-media-grid-4{grid-template-columns:1fr 1fr;grid-template-rows:auto auto}.rts-media-grid-4 .rts-media-image-wrapper{aspect-ratio:1/1;min-height:120px}.rts-media-image-wrapper{position:relative;overflow:hidden;border-radius:6px;background:var(--rts-bg-secondary, #1e1e2e);display:flex;align-items:center;justify-content:center}.rts-media-placeholder{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#1a1a2af5,#12121cfa);border:1px solid rgba(255,255,255,.06);border-radius:6px;z-index:2;overflow:hidden;pointer-events:none}.rts-media-placeholder .rts-media-placeholder-shimmer{position:absolute;top:0;left:-150%;width:150%;height:100%;background:linear-gradient(90deg,transparent 0%,rgba(255,255,255,.03) 30%,rgba(168,85,247,.12) 50%,rgba(255,255,255,.03) 70%,transparent 100%);animation:rts-media-shimmer 2.2s infinite ease-in-out}.rts-media-placeholder .rts-media-placeholder-content{position:relative;z-index:3;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:12px;text-align:center;max-width:90%}.rts-media-placeholder .rts-media-placeholder-icon-wrap{width:42px;height:42px;border-radius:50%;background:#6c5ce726;border:1px solid rgba(168,85,247,.35);display:flex;align-items:center;justify-content:center;animation:rts-media-pulse 2s infinite ease-in-out;box-shadow:0 4px 16px #6c5ce733}.rts-media-placeholder .rts-media-placeholder-icon{font-size:22px;color:var(--rts-accent-light, #c084fc)}.rts-media-placeholder .rts-media-placeholder-text{font-size:11px;font-weight:500;color:var(--rts-text-muted, #94a3b8);max-width:180px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.2}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-icon-wrap{background:#ef444426;border-color:#ef444459;animation:none;box-shadow:none}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-icon{color:#ef4444}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-text{color:#f87171}@keyframes rts-media-shimmer{0%{transform:translate(0)}to{transform:translate(200%)}}@keyframes rts-media-pulse{0%,to{transform:scale(1);opacity:.85}50%{transform:scale(1.08);opacity:1;box-shadow:0 0 16px #a855f766}}.rts-media-image-actions{position:absolute;top:8px;right:8px;z-index:5;display:inline-flex;align-items:center;gap:6px}.rts-media-btn-paint{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#e9d5ff;border:1px solid rgba(168,85,247,.45);border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-paint i{font-size:11px;color:#c084fc}.rts-media-btn-paint span{line-height:1}.rts-media-btn-paint:hover{background:#9333eae6;color:#fff;border-color:#a855f7;transform:translateY(-1px) scale(1.04);box-shadow:0 4px 12px #a855f773}.rts-media-btn-paint:hover i{color:#fff}.rts-media-btn-paint:active{transform:translateY(0) scale(.98)}.rts-media-btn-context{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#bae6fd;border:1px solid rgba(56,189,248,.45);border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-context i{font-size:11px;color:#38bdf8}.rts-media-btn-context span{line-height:1}.rts-media-btn-context:hover{background:#0ea5e9e6;color:#fff;border-color:#38bdf8;transform:translateY(-1px) scale(1.04);box-shadow:0 4px 12px #38bdf873}.rts-media-btn-context:hover i{color:#fff}.rts-media-btn-context:active{transform:translateY(0) scale(.98)}.rts-media-btn-download{position:absolute;left:8px;bottom:8px;z-index:5;width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;padding:0;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#d1fae5;border:1px solid rgba(52,211,153,.45);border-radius:50%;cursor:pointer;opacity:.85;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-download i{font-size:12px;color:#34d399}.rts-media-btn-download:hover{opacity:1;background:#10b981e6;border-color:#34d399;transform:translateY(-1px) scale(1.08);box-shadow:0 4px 12px #34d39973}.rts-media-btn-download:hover i{color:#fff}.rts-media-btn-download:active{transform:translateY(0) scale(.96)}.rts-media-btn-download.rts-downloading{cursor:progress;opacity:1}.rts-media-image{width:100%;height:100%;max-height:280px;object-fit:cover;display:block;opacity:0;transition:opacity .25s ease-out,transform .2s ease;cursor:pointer}.rts-media-image.rts-media-image-loaded{opacity:1}.rts-media-image:hover{transform:scale(1.02)}.rts-media-grid-1 .rts-media-image{height:auto;max-height:400px;object-fit:contain}.rts-media-toggle{display:block;width:100%;padding:6px 0;border:none;background:none;color:var(--rts-agent-accent, #8b5cf6);font-size:12px;font-weight:500;cursor:pointer;text-align:center;transition:opacity .15s ease}.rts-media-toggle:hover{opacity:.8}.rts-media-cost{display:flex;justify-content:flex-end;padding-top:4px}\n"] }]
|
|
2020
2248
|
}], propDecorators: { message: [{
|
|
2021
2249
|
type: Input
|
|
2022
2250
|
}], messenger: [{
|
|
@@ -2850,7 +3078,7 @@ class MessageComponent {
|
|
|
2850
3078
|
/* eslint-enable */
|
|
2851
3079
|
}
|
|
2852
3080
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MessageComponent, deps: [{ token: MessageWidgetsService }, { token: i0.ComponentFactoryResolver }, { token: TimeFormatHelperService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2853
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: MessageComponent, isStandalone: true, selector: "rtsee-message", inputs: { chat: "chat", message: "message", member: "member", messenger: "messenger" }, viewQueries: [{ propertyName: "widgetHost", first: true, predicate: WidgetDirective, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"rts-message-container\" #rtsMessageContainer\n [ngClass]=\"{'rts-message-from-me': message.from.id === messenger.clientId}\"
|
|
3081
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: MessageComponent, isStandalone: true, selector: "rtsee-message", inputs: { chat: "chat", message: "message", member: "member", messenger: "messenger" }, viewQueries: [{ propertyName: "widgetHost", first: true, predicate: WidgetDirective, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"rts-message-container\" #rtsMessageContainer\n [ngClass]=\"{\n 'rts-message-from-me': message.from.id === messenger.clientId,\n 'rts-message-is-widget': !!message.widget\n }\">\n <div class=\"rts-messenger-message\">\n @if (message.widget) {\n <ng-template widgetHost></ng-template>\n }\n @if (!message.widget) {\n <p class=\"rts-messenger-message-text\"\n >{{ message.text }}</p>\n }\n <div class=\"rts-messenger-message-info\" #rtsMessageInfo>\n <rtsee-message-time-and-status [message]=\"message\"\n [inChat]=\"true\"\n [hideStatus]=\"message.from.id !== messenger.clientId\"\n ></rtsee-message-time-and-status>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MessageTimeAndStatusComponent, selector: "rtsee-message-time-and-status", inputs: ["message", "hideStatus", "inChat"] }, { kind: "directive", type: WidgetDirective, selector: "[widgetHost]" }] }); }
|
|
2854
3082
|
}
|
|
2855
3083
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MessageComponent, decorators: [{
|
|
2856
3084
|
type: Component,
|
|
@@ -2859,7 +3087,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
2859
3087
|
MessageTimeAndStatusComponent,
|
|
2860
3088
|
WidgetDirective,
|
|
2861
3089
|
RatingComponent,
|
|
2862
|
-
], standalone: true, template: "<div class=\"rts-message-container\" #rtsMessageContainer\n [ngClass]=\"{'rts-message-from-me': message.from.id === messenger.clientId}\"
|
|
3090
|
+
], standalone: true, template: "<div class=\"rts-message-container\" #rtsMessageContainer\n [ngClass]=\"{\n 'rts-message-from-me': message.from.id === messenger.clientId,\n 'rts-message-is-widget': !!message.widget\n }\">\n <div class=\"rts-messenger-message\">\n @if (message.widget) {\n <ng-template widgetHost></ng-template>\n }\n @if (!message.widget) {\n <p class=\"rts-messenger-message-text\"\n >{{ message.text }}</p>\n }\n <div class=\"rts-messenger-message-info\" #rtsMessageInfo>\n <rtsee-message-time-and-status [message]=\"message\"\n [inChat]=\"true\"\n [hideStatus]=\"message.from.id !== messenger.clientId\"\n ></rtsee-message-time-and-status>\n </div>\n </div>\n</div>\n" }]
|
|
2863
3091
|
}], ctorParameters: () => [{ type: MessageWidgetsService }, { type: i0.ComponentFactoryResolver }, { type: TimeFormatHelperService }], propDecorators: { chat: [{
|
|
2864
3092
|
type: Input
|
|
2865
3093
|
}], message: [{
|
|
@@ -2881,12 +3109,24 @@ class MessagesListComponent {
|
|
|
2881
3109
|
this.messagesGroups = [];
|
|
2882
3110
|
this.firstGroupedMessage = null;
|
|
2883
3111
|
this.lastGroupedMessage = null;
|
|
3112
|
+
this.shouldStickToBottom = true;
|
|
3113
|
+
this.resizeObserver = null;
|
|
3114
|
+
this.onMediaCapture = null;
|
|
3115
|
+
this.initialScrollInterval = null;
|
|
2884
3116
|
this.RTSeeChatTypes = RTSeeChatTypes;
|
|
2885
|
-
this.bindScrollFunction =
|
|
2886
|
-
|
|
3117
|
+
this.bindScrollFunction = () => {
|
|
3118
|
+
this.shouldStickToBottom = true;
|
|
3119
|
+
this.scrollToBottom();
|
|
3120
|
+
};
|
|
3121
|
+
this.bindedGroupFunction = () => {
|
|
3122
|
+
this.groupMessages();
|
|
3123
|
+
if (this.shouldStickToBottom) {
|
|
3124
|
+
this.scrollToBottom();
|
|
3125
|
+
}
|
|
3126
|
+
};
|
|
2887
3127
|
}
|
|
2888
3128
|
onWindowScroll(event) {
|
|
2889
|
-
|
|
3129
|
+
this.checkScrollPosition();
|
|
2890
3130
|
const verticalOffset = window.pageYOffset ||
|
|
2891
3131
|
this.document.documentElement.scrollTop ||
|
|
2892
3132
|
this.document.body.scrollTop ||
|
|
@@ -2894,28 +3134,76 @@ class MessagesListComponent {
|
|
|
2894
3134
|
if (window.innerWidth >= COMMON_CONSTANTS.mobileBreakpoint) {
|
|
2895
3135
|
return;
|
|
2896
3136
|
}
|
|
2897
|
-
// You can add logic here, e.g., show a 'scroll to top' button
|
|
2898
3137
|
if (verticalOffset < 300) {
|
|
2899
3138
|
this.onScrolledToTop();
|
|
2900
3139
|
}
|
|
2901
3140
|
}
|
|
3141
|
+
onWindowResize() {
|
|
3142
|
+
if (this.shouldStickToBottom) {
|
|
3143
|
+
this.scrollToBottom();
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
2902
3146
|
ngOnInit() {
|
|
2903
|
-
this.
|
|
2904
|
-
this.chat.on(RTSeeChatEvents.MESSAGE_ADDED, this.bindScrollFunction);
|
|
3147
|
+
this.bindChatEvents(this.chat);
|
|
2905
3148
|
this.groupMessages();
|
|
2906
3149
|
}
|
|
3150
|
+
ngOnChanges(changes) {
|
|
3151
|
+
if (changes['chat'] && !changes['chat'].firstChange) {
|
|
3152
|
+
const prevChat = changes['chat'].previousValue;
|
|
3153
|
+
this.unbindChatEvents(prevChat);
|
|
3154
|
+
this.bindChatEvents(this.chat);
|
|
3155
|
+
this.groupMessages();
|
|
3156
|
+
this.enforceInitialScroll();
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
2907
3159
|
ngAfterViewInit() {
|
|
2908
|
-
if (this.chat
|
|
3160
|
+
if (this.chat?.isStandalone) {
|
|
2909
3161
|
return;
|
|
2910
3162
|
}
|
|
2911
|
-
this.
|
|
3163
|
+
this.enforceInitialScroll();
|
|
3164
|
+
// Observe size changes of the messages list content (e.g. media widgets expanding/collapsing, images loading)
|
|
3165
|
+
if (typeof ResizeObserver !== 'undefined' && this.contentBlock?.nativeElement) {
|
|
3166
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
3167
|
+
if (this.shouldStickToBottom) {
|
|
3168
|
+
this.scrollToBottom();
|
|
3169
|
+
}
|
|
3170
|
+
});
|
|
3171
|
+
this.resizeObserver.observe(this.contentBlock.nativeElement);
|
|
3172
|
+
}
|
|
3173
|
+
// Capture media load/error events on all nested img and video elements
|
|
3174
|
+
if (this.scrollContainer?.nativeElement) {
|
|
3175
|
+
this.onMediaCapture = () => {
|
|
3176
|
+
if (this.shouldStickToBottom) {
|
|
3177
|
+
this.scrollToBottom();
|
|
3178
|
+
}
|
|
3179
|
+
};
|
|
3180
|
+
this.scrollContainer.nativeElement.addEventListener('load', this.onMediaCapture, true);
|
|
3181
|
+
this.scrollContainer.nativeElement.addEventListener('error', this.onMediaCapture, true);
|
|
3182
|
+
}
|
|
2912
3183
|
}
|
|
2913
3184
|
onMessageSent() {
|
|
3185
|
+
this.shouldStickToBottom = true;
|
|
2914
3186
|
this.scrollToBottom();
|
|
2915
3187
|
}
|
|
2916
3188
|
ngOnDestroy() {
|
|
2917
|
-
this.
|
|
2918
|
-
|
|
3189
|
+
this.unbindChatEvents(this.chat);
|
|
3190
|
+
if (this.initialScrollInterval) {
|
|
3191
|
+
clearInterval(this.initialScrollInterval);
|
|
3192
|
+
this.initialScrollInterval = null;
|
|
3193
|
+
}
|
|
3194
|
+
if (this.resizeObserver) {
|
|
3195
|
+
this.resizeObserver.disconnect();
|
|
3196
|
+
this.resizeObserver = null;
|
|
3197
|
+
}
|
|
3198
|
+
if (this.scrollContainer?.nativeElement && this.onMediaCapture) {
|
|
3199
|
+
this.scrollContainer.nativeElement.removeEventListener('load', this.onMediaCapture, true);
|
|
3200
|
+
this.scrollContainer.nativeElement.removeEventListener('error', this.onMediaCapture, true);
|
|
3201
|
+
this.onMediaCapture = null;
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
onContainerScroll(event) {
|
|
3205
|
+
this.checkScrollPosition();
|
|
3206
|
+
this.onScroll();
|
|
2919
3207
|
}
|
|
2920
3208
|
onScrolledToTop() {
|
|
2921
3209
|
if (this.chat.messagesLoadInProgress || this.chat.firstMessageLoaded) {
|
|
@@ -2929,30 +3217,99 @@ class MessagesListComponent {
|
|
|
2929
3217
|
}
|
|
2930
3218
|
this.chat.loadRemoteMessages('front');
|
|
2931
3219
|
}
|
|
2932
|
-
scrollToBottom() {
|
|
2933
|
-
this.
|
|
2934
|
-
if (this.chat.isStandalone) {
|
|
3220
|
+
scrollToBottom(immediate = false) {
|
|
3221
|
+
if (this.chat?.isStandalone) {
|
|
2935
3222
|
return;
|
|
2936
3223
|
}
|
|
2937
|
-
|
|
2938
|
-
if (
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
3224
|
+
const performScroll = () => {
|
|
3225
|
+
if (this.bottomAnchor?.nativeElement) {
|
|
3226
|
+
try {
|
|
3227
|
+
this.bottomAnchor.nativeElement.scrollIntoView({
|
|
3228
|
+
block: 'end',
|
|
3229
|
+
inline: 'nearest',
|
|
3230
|
+
behavior: 'instant',
|
|
3231
|
+
});
|
|
3232
|
+
}
|
|
3233
|
+
catch {
|
|
3234
|
+
// ignore
|
|
3235
|
+
}
|
|
2943
3236
|
}
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
3237
|
+
if (window.innerWidth < COMMON_CONSTANTS.mobileBreakpoint) {
|
|
3238
|
+
const target = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight);
|
|
3239
|
+
window.scrollTo({ top: target, behavior: 'instant' });
|
|
3240
|
+
this.scroller.scrollToPosition([0, target]);
|
|
2947
3241
|
}
|
|
2948
|
-
|
|
2949
|
-
|
|
3242
|
+
if (this.scrollContainer?.nativeElement) {
|
|
3243
|
+
const el = this.scrollContainer.nativeElement;
|
|
3244
|
+
el.scrollTop = el.scrollHeight;
|
|
2950
3245
|
}
|
|
2951
|
-
}
|
|
3246
|
+
};
|
|
3247
|
+
performScroll();
|
|
3248
|
+
if (!immediate) {
|
|
3249
|
+
requestAnimationFrame(() => {
|
|
3250
|
+
performScroll();
|
|
3251
|
+
});
|
|
3252
|
+
setTimeout(() => {
|
|
3253
|
+
if (this.shouldStickToBottom) {
|
|
3254
|
+
performScroll();
|
|
3255
|
+
}
|
|
3256
|
+
}, 50);
|
|
3257
|
+
}
|
|
2952
3258
|
}
|
|
2953
3259
|
formatDate(message) {
|
|
2954
3260
|
return this.timeFormatService.formatDate(message.createdAt);
|
|
2955
3261
|
}
|
|
3262
|
+
checkScrollPosition() {
|
|
3263
|
+
this.shouldStickToBottom = this.isAtBottom();
|
|
3264
|
+
}
|
|
3265
|
+
isAtBottom() {
|
|
3266
|
+
const threshold = 150; // px threshold from bottom
|
|
3267
|
+
if (window.innerWidth < COMMON_CONSTANTS.mobileBreakpoint) {
|
|
3268
|
+
const scrollHeight = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight);
|
|
3269
|
+
const scrollTop = window.pageYOffset ||
|
|
3270
|
+
document.documentElement.scrollTop ||
|
|
3271
|
+
document.body.scrollTop ||
|
|
3272
|
+
0;
|
|
3273
|
+
const clientHeight = window.innerHeight;
|
|
3274
|
+
return scrollHeight - (scrollTop + clientHeight) <= threshold;
|
|
3275
|
+
}
|
|
3276
|
+
else {
|
|
3277
|
+
const el = this.scrollContainer?.nativeElement;
|
|
3278
|
+
if (!el)
|
|
3279
|
+
return true;
|
|
3280
|
+
return el.scrollHeight - (el.scrollTop + el.clientHeight) <= threshold;
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
enforceInitialScroll() {
|
|
3284
|
+
this.shouldStickToBottom = true;
|
|
3285
|
+
this.scrollToBottom();
|
|
3286
|
+
if (this.initialScrollInterval) {
|
|
3287
|
+
clearInterval(this.initialScrollInterval);
|
|
3288
|
+
}
|
|
3289
|
+
let ticks = 0;
|
|
3290
|
+
this.initialScrollInterval = setInterval(() => {
|
|
3291
|
+
ticks++;
|
|
3292
|
+
if (this.shouldStickToBottom) {
|
|
3293
|
+
this.scrollToBottom(true);
|
|
3294
|
+
}
|
|
3295
|
+
if (ticks >= 20 || !this.shouldStickToBottom) {
|
|
3296
|
+
clearInterval(this.initialScrollInterval);
|
|
3297
|
+
this.initialScrollInterval = null;
|
|
3298
|
+
}
|
|
3299
|
+
}, 100);
|
|
3300
|
+
}
|
|
3301
|
+
bindChatEvents(chat) {
|
|
3302
|
+
if (!chat)
|
|
3303
|
+
return;
|
|
3304
|
+
chat.on(RTSeeChatEvents.MESSAGES_LIST_CHANGED, this.bindedGroupFunction);
|
|
3305
|
+
chat.on(RTSeeChatEvents.MESSAGE_ADDED, this.bindScrollFunction);
|
|
3306
|
+
}
|
|
3307
|
+
unbindChatEvents(chat) {
|
|
3308
|
+
if (!chat)
|
|
3309
|
+
return;
|
|
3310
|
+
chat.off(RTSeeChatEvents.MESSAGE_ADDED, this.bindScrollFunction);
|
|
3311
|
+
chat.off(RTSeeChatEvents.MESSAGES_LIST_CHANGED, this.bindedGroupFunction);
|
|
3312
|
+
}
|
|
2956
3313
|
groupMessages() {
|
|
2957
3314
|
if (!this.chat || !this.chat.messages.length) {
|
|
2958
3315
|
this.messagesGroups.length = 0;
|
|
@@ -2999,11 +3356,11 @@ class MessagesListComponent {
|
|
|
2999
3356
|
this.lastGroupedMessage = this.chat.messages[this.chat.messages.length - 1];
|
|
3000
3357
|
}
|
|
3001
3358
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MessagesListComponent, deps: [{ token: TimeFormatHelperService }, { token: i2$1.ViewportScroller }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3002
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: MessagesListComponent, isStandalone: true, selector: "rtsee-messages-list", inputs: { chat: "chat", messenger: "messenger" }, host: { listeners: { "window:scroll": "onWindowScroll($event)" } }, viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollBlock"], descendants: true }], ngImport: i0, template: "<div class=\"rts-messages-list\"\n #scrollBlock\n infiniteScroll\n [infiniteScrollUpDistance]=\"2\"\n [infiniteScrollDistance]=\"2\"\n [infiniteScrollThrottle]=\"50\"\n [scrollWindow]=\"false\"\n (scrolledUp)=\"onScrolledToTop()\"\n (scroll)=\"
|
|
3359
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: MessagesListComponent, isStandalone: true, selector: "rtsee-messages-list", inputs: { chat: "chat", messenger: "messenger" }, host: { listeners: { "window:scroll": "onWindowScroll($event)", "window:resize": "onWindowResize()" } }, viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollBlock"], descendants: true }, { propertyName: "contentBlock", first: true, predicate: ["contentBlock"], descendants: true }, { propertyName: "bottomAnchor", first: true, predicate: ["bottomAnchor"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"rts-messages-list\"\n #scrollBlock\n infiniteScroll\n [infiniteScrollUpDistance]=\"2\"\n [infiniteScrollDistance]=\"2\"\n [infiniteScrollThrottle]=\"50\"\n [scrollWindow]=\"false\"\n (scrolledUp)=\"onScrolledToTop()\"\n (scroll)=\"onContainerScroll($event)\"\n [style.padding-bottom]=\"messenger.chatInputHeight + 'px'\"\n>\n <div class=\"rts-messages-list-inner\" #contentBlock>\n @for (messageGroup of messagesGroups; track messageGroup.id) {\n <div class=\"rts-messenger-message-wrapper\" [ngClass]=\"{\n 'rts-messenger-message-wrapper-with-sender': messageGroup.senderVisible\n }\">\n <div class=\"rts-messenger-message-inset\">\n <img [src]=\"messageGroup.sender.imageUrl\"\n class=\"rts-messenger-message-sender-image\"\n >\n </div>\n\n <div class=\"rts-messenger-message-sender-name\">\n <p>{{messageGroup.sender.name}}</p>\n </div>\n\n @for (message of messageGroup.messages; track message.id) {\n <rtsee-message [message]=\"message\"\n [messenger]=\"messenger\"\n [chat]=\"chat\"\n ></rtsee-message>\n }\n </div>\n }\n <div class=\"rts-messages-list-bottom-anchor\" #bottomAnchor></div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: InfiniteScrollDirective, selector: "[infiniteScroll], [infinite-scroll], [data-infinite-scroll]", inputs: ["infiniteScrollDistance", "infiniteScrollUpDistance", "infiniteScrollThrottle", "infiniteScrollDisabled", "infiniteScrollContainer", "scrollWindow", "immediateCheck", "horizontal", "alwaysCallback", "fromRoot"], outputs: ["scrolled", "scrolledUp"] }, { kind: "component", type: MessageComponent, selector: "rtsee-message", inputs: ["chat", "message", "member", "messenger"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
|
|
3003
3360
|
}
|
|
3004
3361
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MessagesListComponent, decorators: [{
|
|
3005
3362
|
type: Component,
|
|
3006
|
-
args: [{ selector: 'rtsee-messages-list', imports: [InfiniteScrollDirective, MessageComponent, NgClass], standalone: true, template: "<div class=\"rts-messages-list\"\n #scrollBlock\n infiniteScroll\n [infiniteScrollUpDistance]=\"2\"\n [infiniteScrollDistance]=\"2\"\n [infiniteScrollThrottle]=\"50\"\n [scrollWindow]=\"false\"\n (scrolledUp)=\"onScrolledToTop()\"\n (scroll)=\"
|
|
3363
|
+
args: [{ selector: 'rtsee-messages-list', imports: [InfiniteScrollDirective, MessageComponent, NgClass], standalone: true, template: "<div class=\"rts-messages-list\"\n #scrollBlock\n infiniteScroll\n [infiniteScrollUpDistance]=\"2\"\n [infiniteScrollDistance]=\"2\"\n [infiniteScrollThrottle]=\"50\"\n [scrollWindow]=\"false\"\n (scrolledUp)=\"onScrolledToTop()\"\n (scroll)=\"onContainerScroll($event)\"\n [style.padding-bottom]=\"messenger.chatInputHeight + 'px'\"\n>\n <div class=\"rts-messages-list-inner\" #contentBlock>\n @for (messageGroup of messagesGroups; track messageGroup.id) {\n <div class=\"rts-messenger-message-wrapper\" [ngClass]=\"{\n 'rts-messenger-message-wrapper-with-sender': messageGroup.senderVisible\n }\">\n <div class=\"rts-messenger-message-inset\">\n <img [src]=\"messageGroup.sender.imageUrl\"\n class=\"rts-messenger-message-sender-image\"\n >\n </div>\n\n <div class=\"rts-messenger-message-sender-name\">\n <p>{{messageGroup.sender.name}}</p>\n </div>\n\n @for (message of messageGroup.messages; track message.id) {\n <rtsee-message [message]=\"message\"\n [messenger]=\"messenger\"\n [chat]=\"chat\"\n ></rtsee-message>\n }\n </div>\n }\n <div class=\"rts-messages-list-bottom-anchor\" #bottomAnchor></div>\n </div>\n</div>\n" }]
|
|
3007
3364
|
}], ctorParameters: () => [{ type: TimeFormatHelperService }, { type: i2$1.ViewportScroller }, { type: Document, decorators: [{
|
|
3008
3365
|
type: Inject,
|
|
3009
3366
|
args: [DOCUMENT]
|
|
@@ -3014,9 +3371,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
3014
3371
|
}], scrollContainer: [{
|
|
3015
3372
|
type: ViewChild,
|
|
3016
3373
|
args: ['scrollBlock']
|
|
3374
|
+
}], contentBlock: [{
|
|
3375
|
+
type: ViewChild,
|
|
3376
|
+
args: ['contentBlock']
|
|
3377
|
+
}], bottomAnchor: [{
|
|
3378
|
+
type: ViewChild,
|
|
3379
|
+
args: ['bottomAnchor']
|
|
3017
3380
|
}], onWindowScroll: [{
|
|
3018
3381
|
type: HostListener,
|
|
3019
3382
|
args: ['window:scroll', ['$event']]
|
|
3383
|
+
}], onWindowResize: [{
|
|
3384
|
+
type: HostListener,
|
|
3385
|
+
args: ['window:resize']
|
|
3020
3386
|
}] } });
|
|
3021
3387
|
|
|
3022
3388
|
class ChatMemberSuggestionsComponent {
|
|
@@ -3246,6 +3612,12 @@ class ChatInputComponent {
|
|
|
3246
3612
|
get isGroupChat() {
|
|
3247
3613
|
return this.chat.type !== RTSeeChatTypes.INDIVIDUAL;
|
|
3248
3614
|
}
|
|
3615
|
+
startVideoCall() {
|
|
3616
|
+
if (!this.chat) {
|
|
3617
|
+
return;
|
|
3618
|
+
}
|
|
3619
|
+
void this.messenger.initializeVideoCall(this.chat);
|
|
3620
|
+
}
|
|
3249
3621
|
sendMessage() {
|
|
3250
3622
|
const text = this.sendMessageForm.get('message')?.value || '';
|
|
3251
3623
|
const trimmedText = text.trim();
|
|
@@ -3420,11 +3792,11 @@ class ChatInputComponent {
|
|
|
3420
3792
|
}, 0);
|
|
3421
3793
|
}
|
|
3422
3794
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ChatInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3423
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: ChatInputComponent, isStandalone: true, selector: "rtsee-chat-input", inputs: { messenger: "messenger", chat: "chat" }, viewQueries: [{ propertyName: "chatInput", first: true, predicate: ["chatInput"], descendants: true }, { propertyName: "messageTextarea", first: true, predicate: ["messageTextarea"], descendants: true }], ngImport: i0, template: "<div #chatInput\n class=\"rts-chat-input\"\n [class.rts-chat-input-streaming]=\"chat.agentStreaming\"\n>\n <rtsee-tool-use-request [chat]=\"chat\"></rtsee-tool-use-request>\n <rtsee-chat-member-suggestions\n [suggestions]=\"autocomplete.suggestions\"\n [selectedIndex]=\"autocomplete.selectedIndex\"\n [visible]=\"autocomplete.visible\"\n (memberSelected)=\"onMemberSelected($event)\"\n ></rtsee-chat-member-suggestions>\n <rtsee-workflow-suggestions\n [suggestions]=\"workflowAutocomplete.suggestions\"\n [selectedIndex]=\"workflowAutocomplete.selectedIndex\"\n [visible]=\"workflowAutocomplete.visible\"\n (workflowSelected)=\"onWorkflowSelected($event)\"\n ></rtsee-workflow-suggestions>\n @if (agentAccessDenied) {\n <div class=\"rts-agent-access-denied-banner\">\n <span class=\"rts-agent-access-denied-icon\">\uD83D\uDD12</span>\n <span class=\"rts-agent-access-denied-text\">You don't have permission to call agents in this chat.</span>\n <button class=\"rts-agent-access-denied-dismiss\" (click)=\"dismissAccessDenied()\">Got it!</button>\n </div>\n }\n <div class=\"rts-send-message\">\n <form [formGroup]=\"sendMessageForm\" class=\"rts-chat-input-form\">\n <textarea #messageTextarea\n [invalid]=\"false\"\n [autoResize]=\"true\"\n (keydown)=\"onKeyDown($event)\"\n (keyup)=\"onKeyUp($event)\"\n (input)=\"onInput()\"\n [attr.disabled]=\"chat.agentStreaming ? '' : null\"\n formControlName=\"message\"\n pTextarea\n [placeholder]=\"chat.agentStreaming ? 'Agent is responding...' : 'Type a message...'\"\n class=\"rts-chat-input-field\"\n rows=\"1\"\n cols=\"30\"\n ></textarea>\n </form>\n </div>\n @if (chat.agentStreaming) {\n <button (click)=\"stopAgent()\"\n class=\"rts-send-message-button rts-send-message-button-stop\"\n type=\"button\"\n title=\"Stop agent response\"\n >\n <span class=\"pi pi-stop-circle\"></span>\n </button>\n } @else {\n <button (click)=\"sendMessage()\"\n class=\"rts-send-message-button\"\n type=\"submit\"\n >\n <span class=\"pi pi-send\"></span>\n </button>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }, { kind: "component", type: ChatMemberSuggestionsComponent, selector: "rtsee-chat-member-suggestions", inputs: ["suggestions", "selectedIndex", "visible"], outputs: ["memberSelected"] }, { kind: "component", type: ToolUseRequestComponent, selector: "rtsee-tool-use-request", inputs: ["chat"] }, { kind: "component", type: WorkflowSuggestionsComponent, selector: "rtsee-workflow-suggestions", inputs: ["suggestions", "selectedIndex", "visible"], outputs: ["workflowSelected"] }] }); }
|
|
3795
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: ChatInputComponent, isStandalone: true, selector: "rtsee-chat-input", inputs: { messenger: "messenger", chat: "chat" }, viewQueries: [{ propertyName: "chatInput", first: true, predicate: ["chatInput"], descendants: true }, { propertyName: "messageTextarea", first: true, predicate: ["messageTextarea"], descendants: true }], ngImport: i0, template: "<div #chatInput\n class=\"rts-chat-input\"\n [class.rts-chat-input-streaming]=\"chat.agentStreaming\"\n>\n <rtsee-tool-use-request [chat]=\"chat\"></rtsee-tool-use-request>\n <rtsee-chat-member-suggestions\n [suggestions]=\"autocomplete.suggestions\"\n [selectedIndex]=\"autocomplete.selectedIndex\"\n [visible]=\"autocomplete.visible\"\n (memberSelected)=\"onMemberSelected($event)\"\n ></rtsee-chat-member-suggestions>\n <rtsee-workflow-suggestions\n [suggestions]=\"workflowAutocomplete.suggestions\"\n [selectedIndex]=\"workflowAutocomplete.selectedIndex\"\n [visible]=\"workflowAutocomplete.visible\"\n (workflowSelected)=\"onWorkflowSelected($event)\"\n ></rtsee-workflow-suggestions>\n @if (agentAccessDenied) {\n <div class=\"rts-agent-access-denied-banner\">\n <span class=\"rts-agent-access-denied-icon\">\uD83D\uDD12</span>\n <span class=\"rts-agent-access-denied-text\">You don't have permission to call agents in this chat.</span>\n <button class=\"rts-agent-access-denied-dismiss\" (click)=\"dismissAccessDenied()\">Got it!</button>\n </div>\n }\n <button (click)=\"startVideoCall()\"\n class=\"rts-start-call-button\"\n type=\"button\"\n title=\"Start Video Call\"\n >\n <span class=\"material-icons-outlined\">videocam</span>\n </button>\n <div class=\"rts-send-message\">\n <form [formGroup]=\"sendMessageForm\" class=\"rts-chat-input-form\">\n <textarea #messageTextarea\n [invalid]=\"false\"\n [autoResize]=\"true\"\n (keydown)=\"onKeyDown($event)\"\n (keyup)=\"onKeyUp($event)\"\n (input)=\"onInput()\"\n [attr.disabled]=\"chat.agentStreaming ? '' : null\"\n formControlName=\"message\"\n pTextarea\n [placeholder]=\"chat.agentStreaming ? 'Agent is responding...' : 'Type a message...'\"\n class=\"rts-chat-input-field\"\n rows=\"1\"\n cols=\"30\"\n ></textarea>\n </form>\n </div>\n @if (chat.agentStreaming) {\n <button (click)=\"stopAgent()\"\n class=\"rts-send-message-button rts-send-message-button-stop\"\n type=\"button\"\n title=\"Stop agent response\"\n >\n <span class=\"pi pi-stop-circle\"></span>\n </button>\n } @else {\n <button (click)=\"sendMessage()\"\n class=\"rts-send-message-button\"\n type=\"submit\"\n >\n <span class=\"pi pi-send\"></span>\n </button>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }, { kind: "component", type: ChatMemberSuggestionsComponent, selector: "rtsee-chat-member-suggestions", inputs: ["suggestions", "selectedIndex", "visible"], outputs: ["memberSelected"] }, { kind: "component", type: ToolUseRequestComponent, selector: "rtsee-tool-use-request", inputs: ["chat"] }, { kind: "component", type: WorkflowSuggestionsComponent, selector: "rtsee-workflow-suggestions", inputs: ["suggestions", "selectedIndex", "visible"], outputs: ["workflowSelected"] }] }); }
|
|
3424
3796
|
}
|
|
3425
3797
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ChatInputComponent, decorators: [{
|
|
3426
3798
|
type: Component,
|
|
3427
|
-
args: [{ selector: 'rtsee-chat-input', imports: [ReactiveFormsModule, Textarea, ChatMemberSuggestionsComponent, ToolUseRequestComponent, WorkflowSuggestionsComponent], standalone: true, template: "<div #chatInput\n class=\"rts-chat-input\"\n [class.rts-chat-input-streaming]=\"chat.agentStreaming\"\n>\n <rtsee-tool-use-request [chat]=\"chat\"></rtsee-tool-use-request>\n <rtsee-chat-member-suggestions\n [suggestions]=\"autocomplete.suggestions\"\n [selectedIndex]=\"autocomplete.selectedIndex\"\n [visible]=\"autocomplete.visible\"\n (memberSelected)=\"onMemberSelected($event)\"\n ></rtsee-chat-member-suggestions>\n <rtsee-workflow-suggestions\n [suggestions]=\"workflowAutocomplete.suggestions\"\n [selectedIndex]=\"workflowAutocomplete.selectedIndex\"\n [visible]=\"workflowAutocomplete.visible\"\n (workflowSelected)=\"onWorkflowSelected($event)\"\n ></rtsee-workflow-suggestions>\n @if (agentAccessDenied) {\n <div class=\"rts-agent-access-denied-banner\">\n <span class=\"rts-agent-access-denied-icon\">\uD83D\uDD12</span>\n <span class=\"rts-agent-access-denied-text\">You don't have permission to call agents in this chat.</span>\n <button class=\"rts-agent-access-denied-dismiss\" (click)=\"dismissAccessDenied()\">Got it!</button>\n </div>\n }\n <div class=\"rts-send-message\">\n <form [formGroup]=\"sendMessageForm\" class=\"rts-chat-input-form\">\n <textarea #messageTextarea\n [invalid]=\"false\"\n [autoResize]=\"true\"\n (keydown)=\"onKeyDown($event)\"\n (keyup)=\"onKeyUp($event)\"\n (input)=\"onInput()\"\n [attr.disabled]=\"chat.agentStreaming ? '' : null\"\n formControlName=\"message\"\n pTextarea\n [placeholder]=\"chat.agentStreaming ? 'Agent is responding...' : 'Type a message...'\"\n class=\"rts-chat-input-field\"\n rows=\"1\"\n cols=\"30\"\n ></textarea>\n </form>\n </div>\n @if (chat.agentStreaming) {\n <button (click)=\"stopAgent()\"\n class=\"rts-send-message-button rts-send-message-button-stop\"\n type=\"button\"\n title=\"Stop agent response\"\n >\n <span class=\"pi pi-stop-circle\"></span>\n </button>\n } @else {\n <button (click)=\"sendMessage()\"\n class=\"rts-send-message-button\"\n type=\"submit\"\n >\n <span class=\"pi pi-send\"></span>\n </button>\n }\n</div>\n" }]
|
|
3799
|
+
args: [{ selector: 'rtsee-chat-input', imports: [ReactiveFormsModule, Textarea, ChatMemberSuggestionsComponent, ToolUseRequestComponent, WorkflowSuggestionsComponent], standalone: true, template: "<div #chatInput\n class=\"rts-chat-input\"\n [class.rts-chat-input-streaming]=\"chat.agentStreaming\"\n>\n <rtsee-tool-use-request [chat]=\"chat\"></rtsee-tool-use-request>\n <rtsee-chat-member-suggestions\n [suggestions]=\"autocomplete.suggestions\"\n [selectedIndex]=\"autocomplete.selectedIndex\"\n [visible]=\"autocomplete.visible\"\n (memberSelected)=\"onMemberSelected($event)\"\n ></rtsee-chat-member-suggestions>\n <rtsee-workflow-suggestions\n [suggestions]=\"workflowAutocomplete.suggestions\"\n [selectedIndex]=\"workflowAutocomplete.selectedIndex\"\n [visible]=\"workflowAutocomplete.visible\"\n (workflowSelected)=\"onWorkflowSelected($event)\"\n ></rtsee-workflow-suggestions>\n @if (agentAccessDenied) {\n <div class=\"rts-agent-access-denied-banner\">\n <span class=\"rts-agent-access-denied-icon\">\uD83D\uDD12</span>\n <span class=\"rts-agent-access-denied-text\">You don't have permission to call agents in this chat.</span>\n <button class=\"rts-agent-access-denied-dismiss\" (click)=\"dismissAccessDenied()\">Got it!</button>\n </div>\n }\n <button (click)=\"startVideoCall()\"\n class=\"rts-start-call-button\"\n type=\"button\"\n title=\"Start Video Call\"\n >\n <span class=\"material-icons-outlined\">videocam</span>\n </button>\n <div class=\"rts-send-message\">\n <form [formGroup]=\"sendMessageForm\" class=\"rts-chat-input-form\">\n <textarea #messageTextarea\n [invalid]=\"false\"\n [autoResize]=\"true\"\n (keydown)=\"onKeyDown($event)\"\n (keyup)=\"onKeyUp($event)\"\n (input)=\"onInput()\"\n [attr.disabled]=\"chat.agentStreaming ? '' : null\"\n formControlName=\"message\"\n pTextarea\n [placeholder]=\"chat.agentStreaming ? 'Agent is responding...' : 'Type a message...'\"\n class=\"rts-chat-input-field\"\n rows=\"1\"\n cols=\"30\"\n ></textarea>\n </form>\n </div>\n @if (chat.agentStreaming) {\n <button (click)=\"stopAgent()\"\n class=\"rts-send-message-button rts-send-message-button-stop\"\n type=\"button\"\n title=\"Stop agent response\"\n >\n <span class=\"pi pi-stop-circle\"></span>\n </button>\n } @else {\n <button (click)=\"sendMessage()\"\n class=\"rts-send-message-button\"\n type=\"submit\"\n >\n <span class=\"pi pi-send\"></span>\n </button>\n }\n</div>\n" }]
|
|
3428
3800
|
}], propDecorators: { messenger: [{
|
|
3429
3801
|
type: Input
|
|
3430
3802
|
}], chat: [{
|
|
@@ -3437,6 +3809,75 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
3437
3809
|
args: ['messageTextarea']
|
|
3438
3810
|
}] } });
|
|
3439
3811
|
|
|
3812
|
+
class InviteLinkComponent {
|
|
3813
|
+
constructor() {
|
|
3814
|
+
this.inviteUrl = '';
|
|
3815
|
+
this.disclaimer = 'Anyone who opens this link can join the chat.';
|
|
3816
|
+
this.copied = false;
|
|
3817
|
+
this.loading = false;
|
|
3818
|
+
}
|
|
3819
|
+
async ngOnInit() {
|
|
3820
|
+
if (!this.inviteUrl && this.chatId && this.messenger) {
|
|
3821
|
+
await this.loadInviteLink();
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
async loadInviteLink() {
|
|
3825
|
+
if (!this.chatId || !this.messenger)
|
|
3826
|
+
return;
|
|
3827
|
+
this.loading = true;
|
|
3828
|
+
try {
|
|
3829
|
+
const result = await this.messenger.remoteActions.getChatInvitationLink.send({
|
|
3830
|
+
chatId: this.chatId,
|
|
3831
|
+
});
|
|
3832
|
+
if (result?.invitationLink) {
|
|
3833
|
+
this.inviteUrl = result.invitationLink;
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
catch (e) {
|
|
3837
|
+
console.log('Failed to fetch chat invitation link', e);
|
|
3838
|
+
}
|
|
3839
|
+
finally {
|
|
3840
|
+
this.loading = false;
|
|
3841
|
+
}
|
|
3842
|
+
}
|
|
3843
|
+
async copyToClipboard() {
|
|
3844
|
+
try {
|
|
3845
|
+
await navigator.clipboard.writeText(this.inviteUrl);
|
|
3846
|
+
this.copied = true;
|
|
3847
|
+
setTimeout(() => {
|
|
3848
|
+
this.copied = false;
|
|
3849
|
+
}, 2000);
|
|
3850
|
+
}
|
|
3851
|
+
catch {
|
|
3852
|
+
// Fallback for older browsers
|
|
3853
|
+
const textArea = document.createElement('textarea');
|
|
3854
|
+
textArea.value = this.inviteUrl;
|
|
3855
|
+
document.body.appendChild(textArea);
|
|
3856
|
+
textArea.select();
|
|
3857
|
+
document.execCommand('copy');
|
|
3858
|
+
document.body.removeChild(textArea);
|
|
3859
|
+
this.copied = true;
|
|
3860
|
+
setTimeout(() => {
|
|
3861
|
+
this.copied = false;
|
|
3862
|
+
}, 2000);
|
|
3863
|
+
}
|
|
3864
|
+
}
|
|
3865
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: InviteLinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3866
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: InviteLinkComponent, isStandalone: true, selector: "rtsee-invite-link", inputs: { inviteUrl: "inviteUrl", chatId: "chatId", messenger: "messenger", disclaimer: "disclaimer" }, ngImport: i0, template: "<div class=\"rts-invite-link\">\n <div class=\"rts-invite-link-info\">\n <span class=\"rts-invite-link-icon material-icons-outlined\">link</span>\n <div class=\"rts-invite-link-info-text\">\n <h4>Invite Link</h4>\n <p>{{ disclaimer }}</p>\n </div>\n </div>\n\n @if (loading) {\n <div class=\"rts-invite-link-loading\">\n <span class=\"material-icons-outlined rts-spin-icon\">autorenew</span>\n <span>Generating invite link...</span>\n </div>\n } @else {\n <div class=\"rts-invite-link-url-container\">\n <p-inputgroup>\n <input pInputText\n [value]=\"inviteUrl\"\n readonly\n class=\"rts-invite-link-url-input\"\n />\n <p-button [icon]=\"copied ? 'pi pi-check' : 'pi pi-copy'\"\n [severity]=\"copied ? 'success' : 'primary'\"\n (click)=\"copyToClipboard()\"\n [label]=\"copied ? 'Copied!' : 'Copy'\"\n />\n </p-inputgroup>\n </div>\n }\n</div>\n", dependencies: [{ kind: "component", type: Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: InputText, selector: "[pInputText]", inputs: ["pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: InputGroup, selector: "p-inputgroup, p-inputGroup, p-input-group", inputs: ["styleClass"] }] }); }
|
|
3867
|
+
}
|
|
3868
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: InviteLinkComponent, decorators: [{
|
|
3869
|
+
type: Component,
|
|
3870
|
+
args: [{ selector: 'rtsee-invite-link', standalone: true, imports: [Button, InputText, InputGroup], template: "<div class=\"rts-invite-link\">\n <div class=\"rts-invite-link-info\">\n <span class=\"rts-invite-link-icon material-icons-outlined\">link</span>\n <div class=\"rts-invite-link-info-text\">\n <h4>Invite Link</h4>\n <p>{{ disclaimer }}</p>\n </div>\n </div>\n\n @if (loading) {\n <div class=\"rts-invite-link-loading\">\n <span class=\"material-icons-outlined rts-spin-icon\">autorenew</span>\n <span>Generating invite link...</span>\n </div>\n } @else {\n <div class=\"rts-invite-link-url-container\">\n <p-inputgroup>\n <input pInputText\n [value]=\"inviteUrl\"\n readonly\n class=\"rts-invite-link-url-input\"\n />\n <p-button [icon]=\"copied ? 'pi pi-check' : 'pi pi-copy'\"\n [severity]=\"copied ? 'success' : 'primary'\"\n (click)=\"copyToClipboard()\"\n [label]=\"copied ? 'Copied!' : 'Copy'\"\n />\n </p-inputgroup>\n </div>\n }\n</div>\n" }]
|
|
3871
|
+
}], propDecorators: { inviteUrl: [{
|
|
3872
|
+
type: Input
|
|
3873
|
+
}], chatId: [{
|
|
3874
|
+
type: Input
|
|
3875
|
+
}], messenger: [{
|
|
3876
|
+
type: Input
|
|
3877
|
+
}], disclaimer: [{
|
|
3878
|
+
type: Input
|
|
3879
|
+
}] } });
|
|
3880
|
+
|
|
3440
3881
|
class ViewportObserverDirective {
|
|
3441
3882
|
constructor(element) {
|
|
3442
3883
|
this.element = element;
|
|
@@ -3477,6 +3918,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
3477
3918
|
}] } });
|
|
3478
3919
|
|
|
3479
3920
|
class ChatComponent {
|
|
3921
|
+
constructor() {
|
|
3922
|
+
this.showInvite = false;
|
|
3923
|
+
}
|
|
3924
|
+
get isSoleAdmin() {
|
|
3925
|
+
if (!this.chat)
|
|
3926
|
+
return false;
|
|
3927
|
+
const members = this.chat.members || [];
|
|
3928
|
+
if (members.length !== 1)
|
|
3929
|
+
return false;
|
|
3930
|
+
const myClientId = this.messenger?.myPeer?.clientId || this.chat.clientId;
|
|
3931
|
+
const soleMember = members[0];
|
|
3932
|
+
const isMe = soleMember.id === myClientId || soleMember.clientId === myClientId;
|
|
3933
|
+
const isAdmin = soleMember.role === RTSeeChatMemberRoles.ADMIN ||
|
|
3934
|
+
this.chat.creator?.id === myClientId ||
|
|
3935
|
+
this.chat.creator?.clientId === myClientId;
|
|
3936
|
+
return isMe && isAdmin;
|
|
3937
|
+
}
|
|
3938
|
+
toggleInvite() {
|
|
3939
|
+
this.showInvite = !this.showInvite;
|
|
3940
|
+
}
|
|
3480
3941
|
onInViewPortChange(value) {
|
|
3481
3942
|
console.log('Received inViewPortChange', value);
|
|
3482
3943
|
this.chat.isInViewPort = value;
|
|
@@ -3503,16 +3964,17 @@ class ChatComponent {
|
|
|
3503
3964
|
}
|
|
3504
3965
|
}
|
|
3505
3966
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3506
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
3967
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: ChatComponent, isStandalone: true, selector: "rtsee-chat", inputs: { chat: "chat", chatData: "chatData", entity: "entity", messenger: "messenger" }, ngImport: i0, template: "<div class=\"rts-chat\"\n [ngClass]=\"{\n 'rts-chat-standalone': chat.isStandalone,\n 'rts-chat-in-viewport': chat.isInViewPort\n }\"\n rtseeViewportObserver\n (inViewport)=\"onInViewPortChange($event)\"\n>\n @if (isSoleAdmin) {\n <div class=\"rts-chat-sole-admin-banner\">\n <div class=\"rts-chat-sole-admin-header\">\n <div class=\"rts-chat-sole-admin-badge\">\n <span class=\"material-icons-outlined\">group_add</span>\n </div>\n <div class=\"rts-chat-sole-admin-info\">\n <h4>You are the only member in this chat</h4>\n <p>Invite people to start chatting</p>\n </div>\n <button class=\"rts-btn rts-btn-primary rts-chat-invite-toggle-btn\"\n (click)=\"toggleInvite()\"\n type=\"button\"\n >\n <span class=\"material-icons-outlined\">{{ showInvite ? 'expand_less' : 'person_add' }}</span>\n <span>{{ showInvite ? 'Hide Invite' : 'Invite to Chat' }}</span>\n </button>\n </div>\n\n @if (showInvite) {\n <div class=\"rts-chat-sole-admin-invite\">\n <rtsee-invite-link\n [chatId]=\"chat.id\"\n [messenger]=\"messenger\"\n ></rtsee-invite-link>\n </div>\n }\n </div>\n }\n\n <div class=\"rts-chat-footer-container\">\n <div class=\"rts-chat-footer-container-inner\">\n <rtsee-chat-input [messenger]=\"messenger\" [chat]=\"chat\"></rtsee-chat-input>\n </div>\n </div>\n <div class=\"rts-chat-body-container\">\n <rtsee-messages-list [messenger]=\"messenger\" [chat]=\"chat\"></rtsee-messages-list>\n </div>\n</div>\n", dependencies: [{ kind: "component", type: MessagesListComponent, selector: "rtsee-messages-list", inputs: ["chat", "messenger"] }, { kind: "component", type: ChatInputComponent, selector: "rtsee-chat-input", inputs: ["messenger", "chat"] }, { kind: "component", type: InviteLinkComponent, selector: "rtsee-invite-link", inputs: ["inviteUrl", "chatId", "messenger", "disclaimer"] }, { kind: "directive", type: ViewportObserverDirective, selector: "[rtseeViewportObserver]", outputs: ["inViewport"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
|
|
3507
3968
|
}
|
|
3508
3969
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ChatComponent, decorators: [{
|
|
3509
3970
|
type: Component,
|
|
3510
3971
|
args: [{ selector: 'rtsee-chat', imports: [
|
|
3511
3972
|
MessagesListComponent,
|
|
3512
3973
|
ChatInputComponent,
|
|
3974
|
+
InviteLinkComponent,
|
|
3513
3975
|
ViewportObserverDirective,
|
|
3514
3976
|
NgClass,
|
|
3515
|
-
], standalone: true, template: "<div class=\"rts-chat\"\n [ngClass]=\"{\n 'rts-chat-standalone': chat.isStandalone,\n 'rts-chat-in-viewport': chat.isInViewPort\n }\"\n rtseeViewportObserver\n (inViewport)=\"onInViewPortChange($event)\"\n>\n <div class=\"rts-chat-footer-container\">\n <div class=\"rts-chat-footer-container-inner\">\n <rtsee-chat-input [messenger]=\"messenger\" [chat]=\"chat\"></rtsee-chat-input>\n </div>\n </div>\n <div class=\"rts-chat-body-container\">\n <rtsee-messages-list [messenger]=\"messenger\" [chat]=\"chat\"></rtsee-messages-list>\n </div>\n</div>\n" }]
|
|
3977
|
+
], standalone: true, template: "<div class=\"rts-chat\"\n [ngClass]=\"{\n 'rts-chat-standalone': chat.isStandalone,\n 'rts-chat-in-viewport': chat.isInViewPort\n }\"\n rtseeViewportObserver\n (inViewport)=\"onInViewPortChange($event)\"\n>\n @if (isSoleAdmin) {\n <div class=\"rts-chat-sole-admin-banner\">\n <div class=\"rts-chat-sole-admin-header\">\n <div class=\"rts-chat-sole-admin-badge\">\n <span class=\"material-icons-outlined\">group_add</span>\n </div>\n <div class=\"rts-chat-sole-admin-info\">\n <h4>You are the only member in this chat</h4>\n <p>Invite people to start chatting</p>\n </div>\n <button class=\"rts-btn rts-btn-primary rts-chat-invite-toggle-btn\"\n (click)=\"toggleInvite()\"\n type=\"button\"\n >\n <span class=\"material-icons-outlined\">{{ showInvite ? 'expand_less' : 'person_add' }}</span>\n <span>{{ showInvite ? 'Hide Invite' : 'Invite to Chat' }}</span>\n </button>\n </div>\n\n @if (showInvite) {\n <div class=\"rts-chat-sole-admin-invite\">\n <rtsee-invite-link\n [chatId]=\"chat.id\"\n [messenger]=\"messenger\"\n ></rtsee-invite-link>\n </div>\n }\n </div>\n }\n\n <div class=\"rts-chat-footer-container\">\n <div class=\"rts-chat-footer-container-inner\">\n <rtsee-chat-input [messenger]=\"messenger\" [chat]=\"chat\"></rtsee-chat-input>\n </div>\n </div>\n <div class=\"rts-chat-body-container\">\n <rtsee-messages-list [messenger]=\"messenger\" [chat]=\"chat\"></rtsee-messages-list>\n </div>\n</div>\n" }]
|
|
3516
3978
|
}], propDecorators: { chat: [{
|
|
3517
3979
|
type: Input
|
|
3518
3980
|
}], chatData: [{
|
|
@@ -4413,11 +4875,11 @@ class PresentationHeaderComponent {
|
|
|
4413
4875
|
this.RTSeeTextLabelKeys = RTSeeTextLabelKeys;
|
|
4414
4876
|
}
|
|
4415
4877
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: PresentationHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4416
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: PresentationHeaderComponent, isStandalone: true, selector: "rtsee-presentation-header", inputs: { presentation: "presentation" }, ngImport: i0, template: "<header class=\"rtsee-presentation-header\"\n [ngClass]=\"{\n 'rtsee-presentation-header-convertable': presentation.activeStory,\n 'rtsee-presentation-header-visible': presentation.activeStory?.headerVisible\n }\"\n>\n @if (!presentation.activeStory && !presentation.openedStoryDetails) {\n <div class=\"rtsee-presentations-header-title\">\n <div class=\"rtsee-presentations-header-title-overlay\"></div>\n <div class=\"rtsee-presentations-header-title-content\">\n <h1>{{presentation.labels[RTSeeTextLabelKeys.PRESENTATION_TITLE_LABEL]}}</h1>\n <p class=\"rtsee-presentations-header-title-subtitle\">\n <span>{{presentation.labels[RTSeeTextLabelKeys.PRESENTATION_SUBTITLE_LABEL]}}</span>\n </p>\n </div>\n </div>\n }\n\n @if (presentation.
|
|
4878
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: PresentationHeaderComponent, isStandalone: true, selector: "rtsee-presentation-header", inputs: { presentation: "presentation" }, ngImport: i0, template: "<header class=\"rtsee-presentation-header\"\n [ngClass]=\"{\n 'rtsee-presentation-header-convertable': presentation.activeStory,\n 'rtsee-presentation-header-visible': presentation.activeStory?.headerVisible\n }\"\n>\n @if (!presentation.activeStory && !presentation.openedStoryDetails) {\n <div class=\"rtsee-presentations-header-title\">\n <div class=\"rtsee-presentations-header-title-overlay\"></div>\n <div class=\"rtsee-presentations-header-title-content\">\n <h1>{{presentation.labels[RTSeeTextLabelKeys.PRESENTATION_TITLE_LABEL]}}</h1>\n <p class=\"rtsee-presentations-header-title-subtitle\">\n <span>{{presentation.labels[RTSeeTextLabelKeys.PRESENTATION_SUBTITLE_LABEL]}}</span>\n </p>\n </div>\n </div>\n }\n\n @if (presentation.activeStory) {\n <div class=\"rtsee-presentation-header-active-story-info\">\n <div class=\"rtsee-presentation-header-active-story-info-close\"\n [routerLink]=\"[presentation.homePage]\"\n (click)=\"presentation.disableActiveStory()\"\n >\n <i class=\"pi pi-times rtsee-presentation-header-active-story-info-close-icon\"></i>\n </div>\n <div class=\"rtsee-presentation-header-active-story-info-details\">\n <p-chip [label]=\"presentation.activeStory.name\"\n [image]=\"presentation.activeStory.imageUrl || undefined\"\n class=\"rtsee-presentation-header-active-story-name\"\n alt=\"Story image\" />\n </div>\n <div class=\"rtsee-presentation-header-active-story-info-settings\"\n (click)=\"presentation.settings.expand()\"\n >\n <i class=\"pi pi-cog-h rtsee-presentation-header-active-story-info-settings-icon\"></i>\n </div>\n </div>\n }\n @if (presentation.dashboard && !presentation.activeStory && !presentation.isDashboardOpen && !presentation.openedStoryDetails) {\n <p-button [label]=\"presentation.labels[RTSeeTextLabelKeys.DASHBOARD_BUTTON]\"\n (click)=\"presentation.toggleDashboardState()\"\n ></p-button>\n }\n</header>\n", dependencies: [{ kind: "component", type: Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: Chip, selector: "p-chip", inputs: ["label", "icon", "image", "alt", "styleClass", "disabled", "removable", "removeIcon", "chipProps"], outputs: ["onRemove", "onImageError"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
|
|
4417
4879
|
}
|
|
4418
4880
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: PresentationHeaderComponent, decorators: [{
|
|
4419
4881
|
type: Component,
|
|
4420
|
-
args: [{ selector: 'rtsee-presentation-header', imports: [Button, NgClass, Chip, JoinCallComponent, RouterLink], standalone: true, template: "<header class=\"rtsee-presentation-header\"\n [ngClass]=\"{\n 'rtsee-presentation-header-convertable': presentation.activeStory,\n 'rtsee-presentation-header-visible': presentation.activeStory?.headerVisible\n }\"\n>\n @if (!presentation.activeStory && !presentation.openedStoryDetails) {\n <div class=\"rtsee-presentations-header-title\">\n <div class=\"rtsee-presentations-header-title-overlay\"></div>\n <div class=\"rtsee-presentations-header-title-content\">\n <h1>{{presentation.labels[RTSeeTextLabelKeys.PRESENTATION_TITLE_LABEL]}}</h1>\n <p class=\"rtsee-presentations-header-title-subtitle\">\n <span>{{presentation.labels[RTSeeTextLabelKeys.PRESENTATION_SUBTITLE_LABEL]}}</span>\n </p>\n </div>\n </div>\n }\n\n @if (presentation.
|
|
4882
|
+
args: [{ selector: 'rtsee-presentation-header', imports: [Button, NgClass, Chip, JoinCallComponent, RouterLink], standalone: true, template: "<header class=\"rtsee-presentation-header\"\n [ngClass]=\"{\n 'rtsee-presentation-header-convertable': presentation.activeStory,\n 'rtsee-presentation-header-visible': presentation.activeStory?.headerVisible\n }\"\n>\n @if (!presentation.activeStory && !presentation.openedStoryDetails) {\n <div class=\"rtsee-presentations-header-title\">\n <div class=\"rtsee-presentations-header-title-overlay\"></div>\n <div class=\"rtsee-presentations-header-title-content\">\n <h1>{{presentation.labels[RTSeeTextLabelKeys.PRESENTATION_TITLE_LABEL]}}</h1>\n <p class=\"rtsee-presentations-header-title-subtitle\">\n <span>{{presentation.labels[RTSeeTextLabelKeys.PRESENTATION_SUBTITLE_LABEL]}}</span>\n </p>\n </div>\n </div>\n }\n\n @if (presentation.activeStory) {\n <div class=\"rtsee-presentation-header-active-story-info\">\n <div class=\"rtsee-presentation-header-active-story-info-close\"\n [routerLink]=\"[presentation.homePage]\"\n (click)=\"presentation.disableActiveStory()\"\n >\n <i class=\"pi pi-times rtsee-presentation-header-active-story-info-close-icon\"></i>\n </div>\n <div class=\"rtsee-presentation-header-active-story-info-details\">\n <p-chip [label]=\"presentation.activeStory.name\"\n [image]=\"presentation.activeStory.imageUrl || undefined\"\n class=\"rtsee-presentation-header-active-story-name\"\n alt=\"Story image\" />\n </div>\n <div class=\"rtsee-presentation-header-active-story-info-settings\"\n (click)=\"presentation.settings.expand()\"\n >\n <i class=\"pi pi-cog-h rtsee-presentation-header-active-story-info-settings-icon\"></i>\n </div>\n </div>\n }\n @if (presentation.dashboard && !presentation.activeStory && !presentation.isDashboardOpen && !presentation.openedStoryDetails) {\n <p-button [label]=\"presentation.labels[RTSeeTextLabelKeys.DASHBOARD_BUTTON]\"\n (click)=\"presentation.toggleDashboardState()\"\n ></p-button>\n }\n</header>\n" }]
|
|
4421
4883
|
}], propDecorators: { presentation: [{
|
|
4422
4884
|
type: Input
|
|
4423
4885
|
}] } });
|
|
@@ -5489,7 +5951,7 @@ class PresentationSettingsComponent {
|
|
|
5489
5951
|
this.window = window;
|
|
5490
5952
|
}
|
|
5491
5953
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: PresentationSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
5492
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
5954
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: PresentationSettingsComponent, isStandalone: true, selector: "rtsee-presentation-settings", inputs: { settings: "settings", presentation: "presentation" }, ngImport: i0, template: "<div class=\"rtsee-presentation-settings\">\n <div class=\"rtsee-presentation-settings-header\">\n <p-chip label=\"Settings\" />\n <p-button label=\"Close\"\n (click)=\"settings.collapse()\"\n class=\"rtsee-presentations-dashboard-close-btn\"\n ></p-button>\n </div>\n <div class=\"rtsee-presentation-sound-settings\">\n <p-fieldset legend=\"Volume\" class=\"rtsee-presentation-sound-settings-fieldset\">\n <div class=\"rtsee-presentation-sound-settings-inner\">\n <div class=\"rtsee-presentation-sound-settings-inner-left\">\n <p-slider [(ngModel)]=\"settings.volume\"\n (onChange)=\"settings.setVolume(settings.volume)\"\n />\n </div>\n <div class=\"rtsee-presentation-sound-settings-inner-right\">\n <p-tag severity=\"info\"\n class=\"rtsee-presentation-sound-value-label\"\n [icon]=\"settings.volume ? 'pi pi-volume-up' : 'pi pi-volume-off'\"\n [value]=\"settings.volume.toString()\"\n [rounded]=\"true\" />\n </div>\n </div>\n </p-fieldset>\n </div>\n <div class=\"rtsee-presentation-font-size-settings\">\n <p-fieldset legend=\"Font Size\">\n <div class=\"rtsee-presentation-font-size-range\">\n <p-slider [(ngModel)]=\"settings.fontSizeOption\"\n (onChange)=\"settings.setFontSize(settings.fontSizeOption)\"\n [min]=\"1\"\n [max]=\"10\"\n />\n </div>\n <p-message severity=\"secondary\" class=\"rtsee-presentation-font-size-example\">\n <span [style.font-size]=\"settings.fontSize + 'rem'\">A long time ago in a galaxy far, far away...</span>\n </p-message>\n </p-fieldset>\n </div>\n</div>\n", dependencies: [{ kind: "component", type: Slider, selector: "p-slider", inputs: ["animate", "min", "max", "orientation", "step", "range", "styleClass", "ariaLabel", "ariaLabelledBy", "tabindex", "autofocus"], outputs: ["onChange", "onSlideEnd"] }, { kind: "component", type: Fieldset, selector: "p-fieldset", inputs: ["legend", "toggleable", "collapsed", "style", "styleClass", "transitionOptions"], outputs: ["collapsedChange", "onBeforeToggle", "onAfterToggle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Chip, selector: "p-chip", inputs: ["label", "icon", "image", "alt", "styleClass", "disabled", "removable", "removeIcon", "chipProps"], outputs: ["onRemove", "onImageError"] }, { kind: "component", type: Message, selector: "p-message", inputs: ["severity", "text", "escape", "style", "styleClass", "closable", "icon", "closeIcon", "life", "showTransitionOptions", "hideTransitionOptions", "size", "variant"], outputs: ["onClose"] }, { kind: "component", type: Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }] }); }
|
|
5493
5955
|
}
|
|
5494
5956
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: PresentationSettingsComponent, decorators: [{
|
|
5495
5957
|
type: Component,
|
|
@@ -5500,10 +5962,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
5500
5962
|
Button,
|
|
5501
5963
|
Chip,
|
|
5502
5964
|
Message,
|
|
5503
|
-
Tag
|
|
5504
|
-
|
|
5505
|
-
JoinCallComponent,
|
|
5506
|
-
], template: "<div class=\"rtsee-presentation-settings\">\n <div class=\"rtsee-presentation-settings-header\">\n <p-chip label=\"Settings\" />\n <p-button label=\"Close\"\n (click)=\"settings.collapse()\"\n class=\"rtsee-presentations-dashboard-close-btn\"\n ></p-button>\n </div>\n\n <div class=\"rtsee-presentation-rooms\">\n <p-fieldset legend=\"Rooms\">\n <p-listbox [options]=\"presentation.rooms\"\n [(ngModel)]=\"presentation.selectedRoom\"\n (onChange)=\"presentation.setActiveRoom()\"\n class=\"rtsee-presentation-settings-rooms-list\"\n >\n <ng-template #item let-room>\n <div class=\"rtsee-presentation-settings-rooms-list-item\">\n\n <img [src]=\"room.imageUrl || COMMON_CONSTANTS.defaultProfileImgUrl\"\n class=\"rtsee-presentation-settings-rooms-list-item-image\" />\n <div>{{ room.name }}</div>\n </div>\n </ng-template>\n </p-listbox>\n @if (presentation.selectedRoom) {\n <rtsee-join-call [room]=\"presentation.selectedRoom\"\n [presentation]=\"presentation\"\n ></rtsee-join-call>\n }\n </p-fieldset>\n </div>\n <div class=\"rtsee-presentation-sound-settings\">\n <p-fieldset legend=\"Volume\" class=\"rtsee-presentation-sound-settings-fieldset\">\n <div class=\"rtsee-presentation-sound-settings-inner\">\n <div class=\"rtsee-presentation-sound-settings-inner-left\">\n <p-slider [(ngModel)]=\"settings.volume\"\n (onChange)=\"settings.setVolume(settings.volume)\"\n />\n </div>\n <div class=\"rtsee-presentation-sound-settings-inner-right\">\n <p-tag severity=\"info\"\n class=\"rtsee-presentation-sound-value-label\"\n [icon]=\"settings.volume ? 'pi pi-volume-up' : 'pi pi-volume-off'\"\n [value]=\"settings.volume.toString()\"\n [rounded]=\"true\" />\n </div>\n </div>\n </p-fieldset>\n </div>\n <div class=\"rtsee-presentation-font-size-settings\">\n <p-fieldset legend=\"Font Size\">\n <div class=\"rtsee-presentation-font-size-range\">\n <p-slider [(ngModel)]=\"settings.fontSizeOption\"\n (onChange)=\"settings.setFontSize(settings.fontSizeOption)\"\n [min]=\"1\"\n [max]=\"10\"\n />\n </div>\n <p-message severity=\"secondary\" class=\"rtsee-presentation-font-size-example\">\n <span [style.font-size]=\"settings.fontSize + 'rem'\">A long time ago in a galaxy far, far away...</span>\n </p-message>\n </p-fieldset>\n </div>\n</div>\n" }]
|
|
5965
|
+
Tag
|
|
5966
|
+
], template: "<div class=\"rtsee-presentation-settings\">\n <div class=\"rtsee-presentation-settings-header\">\n <p-chip label=\"Settings\" />\n <p-button label=\"Close\"\n (click)=\"settings.collapse()\"\n class=\"rtsee-presentations-dashboard-close-btn\"\n ></p-button>\n </div>\n <div class=\"rtsee-presentation-sound-settings\">\n <p-fieldset legend=\"Volume\" class=\"rtsee-presentation-sound-settings-fieldset\">\n <div class=\"rtsee-presentation-sound-settings-inner\">\n <div class=\"rtsee-presentation-sound-settings-inner-left\">\n <p-slider [(ngModel)]=\"settings.volume\"\n (onChange)=\"settings.setVolume(settings.volume)\"\n />\n </div>\n <div class=\"rtsee-presentation-sound-settings-inner-right\">\n <p-tag severity=\"info\"\n class=\"rtsee-presentation-sound-value-label\"\n [icon]=\"settings.volume ? 'pi pi-volume-up' : 'pi pi-volume-off'\"\n [value]=\"settings.volume.toString()\"\n [rounded]=\"true\" />\n </div>\n </div>\n </p-fieldset>\n </div>\n <div class=\"rtsee-presentation-font-size-settings\">\n <p-fieldset legend=\"Font Size\">\n <div class=\"rtsee-presentation-font-size-range\">\n <p-slider [(ngModel)]=\"settings.fontSizeOption\"\n (onChange)=\"settings.setFontSize(settings.fontSizeOption)\"\n [min]=\"1\"\n [max]=\"10\"\n />\n </div>\n <p-message severity=\"secondary\" class=\"rtsee-presentation-font-size-example\">\n <span [style.font-size]=\"settings.fontSize + 'rem'\">A long time ago in a galaxy far, far away...</span>\n </p-message>\n </p-fieldset>\n </div>\n</div>\n" }]
|
|
5507
5967
|
}], propDecorators: { settings: [{
|
|
5508
5968
|
type: Input
|
|
5509
5969
|
}], presentation: [{
|