@arthakosh/chat-widget 0.2.30 → 0.2.33

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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, signal, Inject, Injectable, EventEmitter, input, inject, ViewChild, Output, Input, Component, effect, ViewEncapsulation, computed, NgModule } from '@angular/core';
2
+ import { InjectionToken, Inject, Injectable, signal, EventEmitter, input, inject, ViewChild, Output, Input, Component, effect, ViewEncapsulation, computed, NgModule } from '@angular/core';
3
3
  import * as i1$1 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
5
  import * as i2 from '@angular/forms';
@@ -30,99 +30,6 @@ import { trigger, transition, style, animate } from '@angular/animations';
30
30
  // chat.tokens.ts
31
31
  const CHAT_CONFIG = new InjectionToken('CHAT_CONFIG');
32
32
 
33
- class ChatService {
34
- constructor(http, config) {
35
- this.http = http;
36
- this.config = config;
37
- // private baseUrl = `https://coreuatarthkosh.sarjak.com`;
38
- this.currentUser = signal(null);
39
- this.userList = signal([]);
40
- this.archiveRoomId = signal('');
41
- this.notifications = signal([]);
42
- this.hasNewNotification = signal(false);
43
- }
44
- get baseUrl() {
45
- return this.config.apiBaseUrl;
46
- }
47
- /* ---------------- CHAT ROOMS ---------------- */
48
- createRoom(roomId, name, metadata, createdBy, username, chatUsers) {
49
- return this.http.post(`${this.baseUrl}/api/chat/chatrooms/create/${roomId}/`, {
50
- name,
51
- metadata,
52
- createdBy,
53
- username,
54
- chatUsers
55
- });
56
- }
57
- getRoomUsers(roomId) {
58
- return this.http.get(`${this.baseUrl}/api/chat/chatrooms/get_user/${roomId}/users/`).pipe(map(list => list.map(u => ({
59
- id: u.UserId,
60
- username: u.UserName
61
- }))));
62
- }
63
- addUserToRoom(roomId, userId, userName, createdBy) {
64
- return this.http.post(`${this.baseUrl}/api/chat/chatrooms/add_user/${roomId}/users/add/`, {
65
- userId,
66
- userName,
67
- createdBy
68
- });
69
- }
70
- /* ---------------- MESSAGES ---------------- */
71
- getMessages(roomId, since) {
72
- const params = {};
73
- if (since)
74
- params.since = since;
75
- return this.http.get(`${this.baseUrl}/api/chat/messages/rooms/all_messages/${roomId}/`, { params }).pipe(map(list => list.map(m => ({
76
- id: m.id,
77
- chatRoomId: m.chatRoomId,
78
- senderId: m.senderId,
79
- senderName: m.senderName,
80
- content: m.content,
81
- replyToMessageId: m.replyToMessageId,
82
- createdAt: m.createdAt,
83
- hasAttachment: m.hasAttachment ?? false,
84
- }))));
85
- }
86
- sendMessage(roomId, payload) {
87
- return this.http.post(`${this.baseUrl}/api/chat/messages/rooms/send/${roomId}/`, payload).pipe(map(m => ({
88
- id: m.id,
89
- chatRoomId: m.chatRoomId,
90
- senderId: m.senderId,
91
- senderName: m.senderName,
92
- content: m.content,
93
- replyToMessageId: m.replyToMessageId,
94
- createdAt: m.createdAt,
95
- hasAttachment: m.hasAttachment ?? false,
96
- })));
97
- }
98
- getRooms(loggedInUserId, chatRoomId) {
99
- let url = `${this.baseUrl}/api/chat/chatrooms/${loggedInUserId}`;
100
- if (chatRoomId) {
101
- url += `/${chatRoomId}`;
102
- }
103
- return this.http.get(`${url}/`);
104
- }
105
- archiveRoom(roomId) {
106
- return this.http.delete(`${this.baseUrl}/api/chat/chatrooms/archive/${roomId}/`);
107
- }
108
- notifyArrival() {
109
- this.hasNewNotification.set(true);
110
- // auto-reset after animation
111
- setTimeout(() => {
112
- this.hasNewNotification.set(false);
113
- }, 1200);
114
- }
115
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ChatService, deps: [{ token: i1.HttpClient }, { token: CHAT_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable }); }
116
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ChatService, providedIn: 'root' }); }
117
- }
118
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ChatService, decorators: [{
119
- type: Injectable,
120
- args: [{ providedIn: 'root' }]
121
- }], ctorParameters: () => [{ type: i1.HttpClient }, { type: undefined, decorators: [{
122
- type: Inject,
123
- args: [CHAT_CONFIG]
124
- }] }] });
125
-
126
33
  class SocketService {
127
34
  constructor(config) {
128
35
  this.config = config;
@@ -214,6 +121,156 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
214
121
  args: [CHAT_CONFIG]
215
122
  }] }] });
216
123
 
124
+ class ChatService {
125
+ constructor(http, config, socket) {
126
+ this.http = http;
127
+ this.config = config;
128
+ this.socket = socket;
129
+ // private baseUrl = `https://coreuatarthkosh.sarjak.com`;
130
+ this.currentUser = signal(null);
131
+ this.userList = signal([]);
132
+ this.archiveRoomId = signal('');
133
+ this.notifications = signal([]);
134
+ this.hasNewNotification = signal(false);
135
+ this.listenForNotifications();
136
+ }
137
+ get baseUrl() {
138
+ return this.config.apiBaseUrl;
139
+ }
140
+ /* ---------------- CHAT ROOMS ---------------- */
141
+ createRoom(roomId, name, metadata, createdBy, username, chatUsers) {
142
+ return this.http.post(`${this.baseUrl}/api/chat/chatrooms/create/${roomId}/`, {
143
+ name,
144
+ metadata,
145
+ createdBy,
146
+ username,
147
+ chatUsers
148
+ });
149
+ }
150
+ getRoomUsers(roomId) {
151
+ return this.http.get(`${this.baseUrl}/api/chat/chatrooms/get_user/${roomId}/users/`).pipe(map(list => list.map(u => ({
152
+ id: u.UserId,
153
+ username: u.UserName
154
+ }))));
155
+ }
156
+ addUserToRoom(roomId, userId, userName, createdBy) {
157
+ return this.http.post(`${this.baseUrl}/api/chat/chatrooms/add_user/${roomId}/users/add/`, {
158
+ userId,
159
+ userName,
160
+ createdBy
161
+ });
162
+ }
163
+ /* ---------------- MESSAGES ---------------- */
164
+ getMessages(roomId, since) {
165
+ const params = {};
166
+ if (since)
167
+ params.since = since;
168
+ return this.http.get(`${this.baseUrl}/api/chat/messages/rooms/all_messages/${roomId}/`, { params }).pipe(map(list => list.map(m => ({
169
+ id: m.id,
170
+ chatRoomId: m.chatRoomId,
171
+ senderId: m.senderId,
172
+ senderName: m.senderName,
173
+ content: m.content,
174
+ replyToMessageId: m.replyToMessageId,
175
+ createdAt: m.createdAt,
176
+ hasAttachment: m.hasAttachment ?? false,
177
+ }))));
178
+ }
179
+ sendMessage(roomId, payload) {
180
+ return this.http.post(`${this.baseUrl}/api/chat/messages/rooms/send/${roomId}/`, payload).pipe(map(m => ({
181
+ id: m.id,
182
+ chatRoomId: m.chatRoomId,
183
+ senderId: m.senderId,
184
+ senderName: m.senderName,
185
+ content: m.content,
186
+ replyToMessageId: m.replyToMessageId,
187
+ createdAt: m.createdAt,
188
+ hasAttachment: m.hasAttachment ?? false,
189
+ })));
190
+ }
191
+ getRooms(loggedInUserId, chatRoomId) {
192
+ let url = `${this.baseUrl}/api/chat/chatrooms/${loggedInUserId}`;
193
+ if (chatRoomId) {
194
+ url += `/${chatRoomId}`;
195
+ }
196
+ return this.http.get(`${url}/`);
197
+ }
198
+ archiveRoom(roomId) {
199
+ return this.http.delete(`${this.baseUrl}/api/chat/chatrooms/archive/${roomId}/`);
200
+ }
201
+ listenForNotifications() {
202
+ // 🔔 ADDED TO ROOM — only added user
203
+ this.socket.on('addedToRoom').subscribe((msg) => {
204
+ if (msg.addedUserId !== this.currentUser().userId)
205
+ return;
206
+ this.upsertNotification({
207
+ roomId: msg.chatRoomId,
208
+ roomName: msg.roomName,
209
+ type: 'added'
210
+ });
211
+ });
212
+ // 🔔 NEW MESSAGE — everyone EXCEPT sender
213
+ this.socket.on('newMessage').subscribe((msg) => {
214
+ if (msg.senderId === this.currentUser().userId)
215
+ return;
216
+ this.upsertNotification({
217
+ roomId: msg.chatRoomId,
218
+ type: 'message',
219
+ lastMessage: msg.content
220
+ });
221
+ });
222
+ }
223
+ upsertNotification(data) {
224
+ this.notifications.update(list => {
225
+ const index = list.findIndex(n => n.roomId === data.roomId);
226
+ if (index !== -1) {
227
+ const n = list[index];
228
+ list[index] = {
229
+ ...n,
230
+ unreadCount: n.unreadCount + 1,
231
+ lastMessage: data.lastMessage ?? n.lastMessage,
232
+ timestamp: Date.now()
233
+ };
234
+ return [...list];
235
+ }
236
+ return [
237
+ ...list,
238
+ {
239
+ roomId: data.roomId,
240
+ roomName: data.roomName,
241
+ type: data.type,
242
+ unreadCount: 1,
243
+ lastMessage: data.lastMessage,
244
+ timestamp: Date.now()
245
+ }
246
+ ];
247
+ });
248
+ this.notifyArrival();
249
+ }
250
+ clearRoom(roomId) {
251
+ this.notifications.update(list => list.filter(n => n.roomId !== roomId));
252
+ }
253
+ clearAll() {
254
+ this.notifications.set([]);
255
+ }
256
+ notifyArrival() {
257
+ this.hasNewNotification.set(true);
258
+ // auto-reset after animation
259
+ setTimeout(() => {
260
+ this.hasNewNotification.set(false);
261
+ }, 1200);
262
+ }
263
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ChatService, deps: [{ token: i1.HttpClient }, { token: CHAT_CONFIG }, { token: SocketService }], target: i0.ɵɵFactoryTarget.Injectable }); }
264
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ChatService, providedIn: 'root' }); }
265
+ }
266
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ChatService, decorators: [{
267
+ type: Injectable,
268
+ args: [{ providedIn: 'root' }]
269
+ }], ctorParameters: () => [{ type: i1.HttpClient }, { type: undefined, decorators: [{
270
+ type: Inject,
271
+ args: [CHAT_CONFIG]
272
+ }] }, { type: SocketService }] });
273
+
217
274
  class ChatWindowComponent {
218
275
  constructor() {
219
276
  this.header = 'Chat';
@@ -265,12 +322,6 @@ class ChatWindowComponent {
265
322
  this.socket.on('newMessage').subscribe(msg => {
266
323
  if (msg.chatRoomId === this.chatRoomId) {
267
324
  this.messages.update(list => [...list, msg]);
268
- this.upsertNotification({
269
- roomId: msg.chatRoomId,
270
- // roomName: msg.roomName,
271
- type: 'message',
272
- // lastMessage: msg.message
273
- });
274
325
  }
275
326
  // if (msg.senderId !== this.chatService.currentUser().userId) {
276
327
  // this.messageService.add({
@@ -296,11 +347,6 @@ class ChatWindowComponent {
296
347
  if (msg.chatRoomId === this.chatRoomId) {
297
348
  this.loadUsers();
298
349
  console.log('addedToRoom', msg);
299
- this.upsertNotification({
300
- roomId: msg.chatRoomId,
301
- roomName: msg.roomName,
302
- type: 'added'
303
- });
304
350
  }
305
351
  });
306
352
  this.socket.on('userAlreadyInRoom').subscribe((msg) => {
@@ -376,39 +422,6 @@ class ChatWindowComponent {
376
422
  closeChat() {
377
423
  this.onClose.emit();
378
424
  }
379
- upsertNotification(data) {
380
- this.chatService.notifications.update(list => {
381
- const index = list.findIndex(n => n.roomId === data.roomId);
382
- if (index !== -1) {
383
- const n = list[index];
384
- list[index] = {
385
- ...n,
386
- unreadCount: n.unreadCount + 1,
387
- lastMessage: data.lastMessage ?? n.lastMessage,
388
- timestamp: Date.now()
389
- };
390
- return [...list];
391
- }
392
- return [
393
- ...list,
394
- {
395
- roomId: data.roomId,
396
- roomName: data.roomName,
397
- type: data.type,
398
- unreadCount: 1,
399
- lastMessage: data.lastMessage,
400
- timestamp: Date.now()
401
- }
402
- ];
403
- });
404
- this.chatService.notifyArrival();
405
- }
406
- clearRoom(roomId) {
407
- this.chatService.notifications.update(list => list.filter(n => n.roomId !== roomId));
408
- }
409
- clearAll() {
410
- this.chatService.notifications.set([]);
411
- }
412
425
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ChatWindowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
413
426
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: ChatWindowComponent, isStandalone: true, selector: "app-chat-window", inputs: { chatRoomId: { classPropertyName: "chatRoomId", publicName: "chatRoomId", isSignal: false, isRequired: false, transformFunction: null }, header: { classPropertyName: "header", publicName: "header", isSignal: false, isRequired: false, transformFunction: null }, metadata: { classPropertyName: "metadata", publicName: "metadata", isSignal: false, isRequired: false, transformFunction: null }, chatUsers: { classPropertyName: "chatUsers", publicName: "chatUsers", isSignal: false, isRequired: false, transformFunction: null }, showActions: { classPropertyName: "showActions", publicName: "showActions", isSignal: false, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: false, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: false, isRequired: false, transformFunction: null }, refreshRoom: { classPropertyName: "refreshRoom", publicName: "refreshRoom", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onArchive: "onArchive", onClose: "onClose" }, providers: [MessageService], viewQueries: [{ propertyName: "messagesContainer", first: true, predicate: ["messagesContainer"], descendants: true }, { propertyName: "reactionOverlay", first: true, predicate: ["reactionOverlay"], descendants: true }], ngImport: i0, template: "<p-toast></p-toast>\r\n\r\n<div [class.dark]=\"theme === 'dark'\" [class.floating]=\"mode === 'floating'\" [class.fullscreen]=\"mode === 'fullscreen'\"\r\n [class.minimized]=\"minimized\" class=\"chat-window\">\r\n <div class=\"chat-header\">\r\n <div style=\"display: flex; flex-direction: column;\">\r\n <div class=\"title\">\r\n <span>{{ header }}</span>\r\n <span *ngIf=\"users().length\">({{ users().length }} {{\r\n users().length == 1 ? 'participant' :\r\n 'participants'\r\n }})</span>\r\n </div>\r\n <div>\r\n <span>{{ metadata }}</span>\r\n </div>\r\n </div>\r\n <div class=\"header-actions\">\r\n <button (click)=\"toggleUsers()\" icon=\"pi pi-users\" pButton pTooltip=\"Toogle Users\"\r\n style=\"font-size: 0.5rem\"></button>\r\n @if (showActions) {\r\n <button (click)=\"toggleMinimize()\" [icon]=\"minimized ? 'pi pi-plus' : 'pi pi-minus'\" pButton\r\n pTooltip=\"Hide/Show\" style=\"font-size: 0.5rem\"></button>\r\n <button (click)=\"archiveChat()\" icon=\"pi pi-trash\" pButton pTooltip=\"Archive\"\r\n style=\"font-size: 0.5rem\"></button>\r\n <button (click)=\"closeChat()\" icon=\"pi pi-times\" pButton pTooltip=\"Close\"\r\n style=\"font-size: 0.5rem\"></button>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"!minimized\" class=\"chat-body\">\r\n <div [class.users-hidden]=\"!showUsers\" class=\"chat-layout\">\r\n <div [class.floating]=\"mode === 'floating'\" [class.fullscreen]=\"mode === 'fullscreen'\" class=\"chat-main\">\r\n <div #messagesContainer [class.loading]=\"loading\" class=\"messages\">\r\n <div *ngFor=\"let msg of messages(); trackBy: trackById\" [ngClass]=\"{ outgoing: msg.senderId === chatService.currentUser().userId, incoming: msg.senderId !== chatService.currentUser().userId }\"\r\n class=\"message-row\">\r\n <div class=\"message-bubble\">\r\n <div class=\"message-meta\">\r\n <span class=\"sender\">\r\n <!-- *ngIf=\"msg.senderId !== chatService.currentUser().userId\" -->\r\n {{ msg.senderName }}\r\n </span>\r\n <!-- <span class=\"time\">\r\n {{ msg.createdAt | date: 'shortTime' }}\r\n </span> -->\r\n </div>\r\n\r\n <!-- MESSAGE TEXT -->\r\n <div class=\"message-content\">\r\n {{ msg.content }}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"input-area\">\r\n <div *ngIf=\"replyTo\" class=\"reply-banner\">\r\n <span>Replying to: {{ replyTo.content | slice: 0:50 }}</span>\r\n <button (click)=\"clearReply()\" class=\"p-button-text p-button-sm\" icon=\"pi pi-times\"\r\n pButton></button>\r\n </div>\r\n\r\n <textarea (keyup.enter)=\"send()\" [(ngModel)]=\"newMessage\" autoResize=\"true\" pInputTextarea\r\n placeholder=\"Type a message\"\r\n rows=\"2\">\r\n </textarea>\r\n\r\n <div class=\"input-actions\">\r\n <button (click)=\"send()\" icon=\"pi pi-send\" label=\"Send\" pButton></button>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"chat-sidebar\">\r\n <div class=\"section-title\">Users</div>\r\n <div class=\"user-list\">\r\n <div *ngFor=\"let u of users()\" class=\"user-row\">\r\n <p-avatar [label]=\"u.username[0]\"></p-avatar>\r\n <span>{{ u.username }}</span>\r\n </div>\r\n </div>\r\n @if (showActions) {\r\n <div class=\"user-list-add\">\r\n <p-dropdown [(ngModel)]=\"selectedUser\" [options]=\"chatService.userList()\"\r\n optionLabel=\"full_name\"\r\n placeholder=\"Select User\"/>\r\n <div style=\"margin-top: 2em;\">\r\n <button (click)=\"addUserToRoom()\" icon=\"pi pi-plus\" label=\"Add\" pButton></button>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n</div>", styles: [".chat-window{width:35vw;max-width:95vw;display:flex;flex-direction:column;background:#fff;border-radius:16px;box-shadow:0 8px 30px #0000001f;overflow:visible;font-size:14px;font-family:Inter,system-ui,sans-serif}.chat-window.minimized{height:auto}.chat-header{display:flex;align-items:center;justify-content:space-between;background:#4f6ef7;color:#fff;padding:10px 14px}.chat-header .title{display:flex;flex-direction:row;gap:6px}.chat-header .title span{font-weight:600;font-size:14px}.chat-header .title small{font-size:11px;opacity:.85}.chat-body{display:flex;flex:1;background:#f5f7fb}.chat-layout{display:flex;flex:1;width:100%;overflow:hidden}.chat-layout.users-hidden .chat-sidebar{transform:translate(100%);opacity:0;pointer-events:none;width:0!important}.chat-layout.users-hidden .chat-main{flex:1 1 100%}.chat-main{flex:1;display:flex;flex-direction:column;background:#f5f7fb;height:70vh}.messages{flex:1;padding:16px 14px;overflow-y:auto;scroll-behavior:smooth}.message-row{display:flex;margin-bottom:14px;animation:messageIn .25s ease-out;position:relative;overflow:visible;padding-top:28px}@keyframes messageIn{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.message-row.incoming{justify-content:flex-start}.message-row.incoming .reaction-bar{left:12px}.message-row.outgoing{justify-content:flex-end}.message-row.outgoing .reaction-bar{right:12px;left:auto}.message-bubble{position:relative;max-width:72%;padding:10px 14px;border-radius:16px;font-size:14px;line-height:1.45;word-break:break-word;overflow:visible}.message-row.incoming .message-bubble{background:#fff;color:#111;border-top-left-radius:6px;box-shadow:0 3px 10px #0000000f;overflow:visible}.message-bubble:after{content:\"\";position:absolute;bottom:-6px;right:8px}.message-row.incoming .message-bubble:after{content:\"\";position:absolute;left:-6px;top:12px;width:0;height:0;border-top:6px solid transparent;border-bottom:6px solid transparent;border-right:6px solid #ffffff}.message-row.outgoing .message-bubble{background:#4f6ef7;color:#fff;border-top-right-radius:6px;overflow:visible}.message-row.outgoing .message-bubble:after{content:\"\";position:absolute;right:-6px;top:12px;width:0;height:0;border-top:6px solid transparent;border-bottom:6px solid transparent;border-left:6px solid #4f6ef7}.message-meta{display:flex;justify-content:flex-end;font-size:10px;opacity:.6;margin-bottom:4px}.typing-indicator{display:inline-flex;align-items:center;gap:4px;padding:8px 12px;background:#fff;border-radius:14px;box-shadow:0 2px 6px #00000014}.typing-indicator span{width:6px;height:6px;background:#999;border-radius:50%;animation:typingDots 1.4s infinite ease-in-out both}.typing-indicator span:nth-child(1){animation-delay:0s}.typing-indicator span:nth-child(2){animation-delay:.2s}.typing-indicator span:nth-child(3){animation-delay:.4s}@keyframes typingDots{0%{transform:translateY(0);opacity:.4}50%{transform:translateY(-4px);opacity:1}to{transform:translateY(0);opacity:.4}}.input-area{background:#fff;padding:10px;border:1px solid #e5e7eb;display:flex;flex-direction:row;justify-content:space-between;gap:5px}.input-area textarea{max-height:60px;overflow:hidden;width:-webkit-fill-available}.reply-banner{background:#eef2ff;border-left:3px solid #4f6ef7;padding:6px 8px;font-size:12px;border-radius:8px;margin-bottom:6px;display:flex;justify-content:space-between}.input-actions{display:flex;justify-content:flex-end;margin-top:6px}.input-actions button{background:#4f6ef7;color:#fff;border-radius:999px;padding:0 16px}.chat-sidebar{background:#fff;border-left:1px solid #e5e7eb;box-shadow:-4px 0 10px #0000000d;display:flex;flex-direction:column}.section-title{font-weight:600;font-size:13px;padding:10px;border-bottom:1px solid #e5e7eb}.user-list{flex:1;overflow-y:auto;padding:8px;max-height:20em}.user-row{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:8px}.user-row:hover{background:#f1f5f9}.user-list-add{padding:10px;border-top:1px solid #e5e7eb}.header-actions{display:flex;align-items:center;gap:6px}.header-actions .p-button{width:32px;height:32px;padding:0;border-radius:50%;background:#ffffff26}.header-actions .p-button:hover{background:#ffffff40}.chat-header{min-height:48px}.header-actions{flex-shrink:0}.header-actions .p-button{transition:background .2s ease,transform .1s ease}.header-actions .p-button:active{transform:scale(.92)}.reaction-bar{position:absolute;top:0;left:12px;transform:translateY(-100%);background:#fff;border-radius:20px;padding:4px 8px;display:none;box-shadow:0 2px 8px #0003;z-index:9999}.message-bubble:hover .reaction-bar{display:flex}.reaction-bar span{cursor:pointer;font-size:16px}.reaction-summary{margin-top:4px;display:flex;gap:6px}.reaction-chip{background:#f1f1f1;padding:2px 6px;border-radius:12px;font-size:12px;position:relative}.reaction-users-tooltip{position:absolute;bottom:130%;left:50%;transform:translate(-50%);white-space:nowrap;background:#222;color:#fff;padding:4px 8px;border-radius:6px;z-index:10000;bottom:calc(100% + 6px)}.message-row,.message-bubble,.reaction-summary{overflow:visible!important}.p-tooltip{z-index:9999!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$1.SlicePipe, name: "slice" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ToastModule }, { kind: "component", type: i3.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "style", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i4.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "label", "icon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain"] }, { kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i5.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "style", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "ngmodule", type: InputTextareaModule }, { kind: "directive", type: i7.InputTextarea, selector: "[pInputTextarea]", inputs: ["autoResize", "variant"], outputs: ["onResize"] }, { kind: "ngmodule", type: ChipModule }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i8.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "autoShowPanelOnPrintableCharacterKeyDown", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: OverlayPanelModule }] }); }
414
427
  }
@@ -644,15 +657,20 @@ class ChatWidgetNotificationComponent {
644
657
  toggle() {
645
658
  this.open = !this.open;
646
659
  }
660
+ openRoom(n) {
661
+ // this.chatLauncher.openRoom(n.roomId);
662
+ this.chatService.clearRoom(n.roomId);
663
+ this.open = false;
664
+ }
647
665
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ChatWidgetNotificationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
648
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: ChatWidgetNotificationComponent, isStandalone: true, selector: "chat-widget-notification", ngImport: i0, template: "<div (click)=\"toggle()\" class=\"notif-icon\">\r\n <i [class.pulse]=\"hasNew()\" class=\"pi pi-bell\">\r\n </i>\r\n\r\n @if (totalCount() > 0) {\r\n <span class=\"dot\"></span>\r\n <span class=\"badge\">\r\n {{ totalCount() }}\r\n </span>\r\n }\r\n</div>\r\n\r\n@if (open) {\r\n <div class=\"notif-panel\">\r\n @for (n of notifications(); track n.roomId) {\r\n <div class=\"notif-item\">\r\n <strong>{{ n.roomName }}</strong>\r\n <p>{{ n.lastMessage || 'You were added to this room' }}</p>\r\n <span class=\"count\">{{ n.unreadCount }}</span>\r\n </div>\r\n }\r\n </div>\r\n}", styles: [".notif-wrapper{position:relative;cursor:pointer}.pi-bell{font-size:1.4rem}.pi-bell.pulse{animation:pulse 1.2s ease-out}@keyframes pulse{0%{transform:scale(1)}40%{transform:scale(1.2)}to{transform:scale(1)}}.dot{position:absolute;top:0;right:2px;width:8px;height:8px;background:red;border-radius:50%}.badge{position:absolute;top:-6px;right:-10px;background:red;color:#fff;font-size:.65rem;padding:2px 6px;border-radius:10px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }] }); }
666
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: ChatWidgetNotificationComponent, isStandalone: true, selector: "chat-widget-notification", ngImport: i0, template: "<div (click)=\"toggle()\" class=\"notif-icon\">\r\n <i class=\"pi pi-bell\" [class.pulse]=\"hasNew()\"></i>\r\n\r\n @if (totalCount() > 0) {\r\n <span class=\"dot\"></span>\r\n <span class=\"badge\">{{ totalCount() }}</span>\r\n }\r\n</div>\r\n\r\n@if (open) {\r\n <div class=\"notif-panel\">\r\n <div class=\"notif-header\">Notifications</div>\r\n\r\n <div class=\"notif-list\">\r\n @for (n of notifications(); track n.roomId) {\r\n <div class=\"notif-item\" (click)=\"openRoom(n)\">\r\n <div class=\"notif-left\">\r\n <i class=\"pi pi-comments\"></i>\r\n </div>\r\n\r\n <div class=\"notif-body\">\r\n <div class=\"notif-title\">{{ n.roomName }}</div>\r\n <div class=\"notif-text\">\r\n {{ n.lastMessage || 'You were added to this room' }}\r\n </div>\r\n </div>\r\n\r\n <span class=\"count\">{{ n.unreadCount }}</span>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n}\r\n", styles: [".notif-icon{position:relative;cursor:pointer;display:flex;align-items:center;justify-content:center}.notif-icon i{font-size:1.4rem;color:#444}.pulse{animation:pulse 1.2s ease-out}@keyframes pulse{0%{transform:scale(1)}40%{transform:scale(1.2)}to{transform:scale(1)}}.dot{position:absolute;top:2px;right:4px;width:8px;height:8px;background:#e53935;border-radius:50%}.badge{position:absolute;top:-6px;right:-10px;background:#e53935;color:#fff;font-size:.65rem;min-width:18px;height:18px;padding:0 5px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-weight:600}.notif-panel{position:absolute;top:42px;right:0;width:320px;background:#fff;border-radius:8px;box-shadow:0 12px 28px #00000026;z-index:9999;overflow:hidden;animation:fadeIn .15s ease-out}@keyframes fadeIn{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.notif-header{padding:12px 16px;font-weight:600;font-size:.9rem;border-bottom:1px solid #eee}.notif-list{max-height:360px;overflow-y:auto}.notif-item{display:flex;align-items:center;padding:12px 14px;cursor:pointer;transition:background .15s ease}.notif-item:hover{background:#f5f7fa}.notif-left{width:36px;height:36px;border-radius:50%;background:#e3f2fd;color:#1976d2;display:flex;align-items:center;justify-content:center;margin-right:12px}.notif-body{flex:1;min-width:0}.notif-title{font-size:.85rem;font-weight:600;color:#222;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.notif-text{font-size:.75rem;color:#666;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.count{background:#1976d2;color:#fff;font-size:.65rem;min-width:20px;height:20px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-weight:600}.notif-item{background:#f0f6ff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }] }); }
649
667
  }
650
668
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ChatWidgetNotificationComponent, decorators: [{
651
669
  type: Component,
652
670
  args: [{ selector: 'chat-widget-notification', standalone: true, imports: [
653
671
  CommonModule,
654
672
  FormsModule
655
- ], template: "<div (click)=\"toggle()\" class=\"notif-icon\">\r\n <i [class.pulse]=\"hasNew()\" class=\"pi pi-bell\">\r\n </i>\r\n\r\n @if (totalCount() > 0) {\r\n <span class=\"dot\"></span>\r\n <span class=\"badge\">\r\n {{ totalCount() }}\r\n </span>\r\n }\r\n</div>\r\n\r\n@if (open) {\r\n <div class=\"notif-panel\">\r\n @for (n of notifications(); track n.roomId) {\r\n <div class=\"notif-item\">\r\n <strong>{{ n.roomName }}</strong>\r\n <p>{{ n.lastMessage || 'You were added to this room' }}</p>\r\n <span class=\"count\">{{ n.unreadCount }}</span>\r\n </div>\r\n }\r\n </div>\r\n}", styles: [".notif-wrapper{position:relative;cursor:pointer}.pi-bell{font-size:1.4rem}.pi-bell.pulse{animation:pulse 1.2s ease-out}@keyframes pulse{0%{transform:scale(1)}40%{transform:scale(1.2)}to{transform:scale(1)}}.dot{position:absolute;top:0;right:2px;width:8px;height:8px;background:red;border-radius:50%}.badge{position:absolute;top:-6px;right:-10px;background:red;color:#fff;font-size:.65rem;padding:2px 6px;border-radius:10px}\n"] }]
673
+ ], template: "<div (click)=\"toggle()\" class=\"notif-icon\">\r\n <i class=\"pi pi-bell\" [class.pulse]=\"hasNew()\"></i>\r\n\r\n @if (totalCount() > 0) {\r\n <span class=\"dot\"></span>\r\n <span class=\"badge\">{{ totalCount() }}</span>\r\n }\r\n</div>\r\n\r\n@if (open) {\r\n <div class=\"notif-panel\">\r\n <div class=\"notif-header\">Notifications</div>\r\n\r\n <div class=\"notif-list\">\r\n @for (n of notifications(); track n.roomId) {\r\n <div class=\"notif-item\" (click)=\"openRoom(n)\">\r\n <div class=\"notif-left\">\r\n <i class=\"pi pi-comments\"></i>\r\n </div>\r\n\r\n <div class=\"notif-body\">\r\n <div class=\"notif-title\">{{ n.roomName }}</div>\r\n <div class=\"notif-text\">\r\n {{ n.lastMessage || 'You were added to this room' }}\r\n </div>\r\n </div>\r\n\r\n <span class=\"count\">{{ n.unreadCount }}</span>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n}\r\n", styles: [".notif-icon{position:relative;cursor:pointer;display:flex;align-items:center;justify-content:center}.notif-icon i{font-size:1.4rem;color:#444}.pulse{animation:pulse 1.2s ease-out}@keyframes pulse{0%{transform:scale(1)}40%{transform:scale(1.2)}to{transform:scale(1)}}.dot{position:absolute;top:2px;right:4px;width:8px;height:8px;background:#e53935;border-radius:50%}.badge{position:absolute;top:-6px;right:-10px;background:#e53935;color:#fff;font-size:.65rem;min-width:18px;height:18px;padding:0 5px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-weight:600}.notif-panel{position:absolute;top:42px;right:0;width:320px;background:#fff;border-radius:8px;box-shadow:0 12px 28px #00000026;z-index:9999;overflow:hidden;animation:fadeIn .15s ease-out}@keyframes fadeIn{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.notif-header{padding:12px 16px;font-weight:600;font-size:.9rem;border-bottom:1px solid #eee}.notif-list{max-height:360px;overflow-y:auto}.notif-item{display:flex;align-items:center;padding:12px 14px;cursor:pointer;transition:background .15s ease}.notif-item:hover{background:#f5f7fa}.notif-left{width:36px;height:36px;border-radius:50%;background:#e3f2fd;color:#1976d2;display:flex;align-items:center;justify-content:center;margin-right:12px}.notif-body{flex:1;min-width:0}.notif-title{font-size:.85rem;font-weight:600;color:#222;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.notif-text{font-size:.75rem;color:#666;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.count{background:#1976d2;color:#fff;font-size:.65rem;min-width:20px;height:20px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-weight:600}.notif-item{background:#f0f6ff}\n"] }]
656
674
  }], ctorParameters: () => [] });
657
675
 
658
676
  class ChatWidgetModule {