+ @for (
+ attachment of orderedAttachments;
+ track attachment.image_url ||
+ attachment.img_url ||
+ attachment.asset_url ||
+ attachment.thumb_url
+ ) {
+
0
+ "
+ [class.str-chat__message-attachment--svg-image]="isSvg(attachment)"
+ >
+ @if (isImage(attachment)) {
+
-
-
-
-
-
- 3
- "
- [class.str-chat__gallery-two-rows]="
- (attachmentContext?.images)!.length > 2
+
+
+
+ }
+ @if (isGallery(attachment)) {
+
-
+
+ 3
+ "
+ [class.str-chat__gallery-two-rows]="
+ (attachmentContext?.images)!.length > 2
"
>
-
-
-
-
-
-
-
-
-
-
+
![]()
+
+ }
+ @if (index === 3 && !isLast) {
+
+ }
+ }
+
+
+ }
+ @if (isVideo(attachment)) {
+
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ attachmentContext.title }}
-
-
- {{ attachmentContext.text }}
-
+
+ }
+ @if (isFile(attachment)) {
+
+
+
-
-
-
-
0">
-
-
-
-
-
-
}
- "
- >
-
+ @if (attachment.actions && attachment.actions.length > 0) {
+
+
+
+
+ @for (action of attachmentContext.actions; track action.value) {
+
+ }
+
+
+
+ }
+
+ }
+ @if (customAttachmentsTemplate) {
+
+ }
-
0">
-
-
-
+ @if (imagesToView && imagesToView.length > 0) {
+
+ }
+
+}
-
+ />
@@ -508,7 +525,7 @@
height: getCarouselImageAttachmentConfiguration(
imagesToView[imagesToViewCurrentIndex],
imgElement
- ).height
+ ).height,
}"
/>
@@ -517,24 +534,24 @@
data-testid="image-modal-prev"
type="button"
[ngStyle]="{
- visibility: isImageModalPrevButtonVisible ? 'visible' : 'hidden'
+ visibility: isImageModalPrevButtonVisible ? 'visible' : 'hidden',
}"
(click)="stepImages(-1)"
(keyup.enter)="stepImages(-1)"
>
-
+
diff --git a/projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.spec.ts b/projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.spec.ts
index 68942e4b..7b88f6d2 100644
--- a/projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.spec.ts
+++ b/projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.spec.ts
@@ -79,18 +79,18 @@ describe('AttachmentListComponent', () => {
queryAttachments = () =>
Array.from(
nativeElement.querySelectorAll(
- '[data-testclass="attachment-container"]'
- )
+ '[data-testclass="attachment-container"]',
+ ),
);
queryImages = () =>
Array.from(nativeElement.querySelectorAll('[data-testclass="image"]'));
queryFileLinks = () =>
Array.from(
- nativeElement.querySelectorAll('[data-testclass="file-link"]')
+ nativeElement.querySelectorAll('[data-testclass="file-link"]'),
);
queryFileNames = () =>
Array.from(
- nativeElement.querySelectorAll('[data-testclass="file-title"]')
+ nativeElement.querySelectorAll('[data-testclass="file-title"]'),
);
queryUrlLinks = () =>
Array.from(nativeElement.querySelectorAll('[data-testclass="url-link"]'));
@@ -98,42 +98,42 @@ describe('AttachmentListComponent', () => {
Array.from(nativeElement.querySelectorAll('[data-testclass="card-img"]'));
queryActions = () =>
Array.from(
- nativeElement.querySelectorAll('[data-testclass="attachment-action"]')
+ nativeElement.querySelectorAll('[data-testclass="attachment-action"]'),
);
queryVoiceMessges = () =>
Array.from(
- nativeElement.querySelectorAll('[data-testclass="voice-recording"]')
+ nativeElement.querySelectorAll('[data-testclass="voice-recording"]'),
);
queryImageModal = () =>
fixture.debugElement.query(By.directive(ModalComponent))
?.componentInstance as ModalComponent;
queryImageModalImage = () =>
nativeElement.querySelector(
- '[data-testid="modal-image"]'
+ '[data-testid="modal-image"]',
) as HTMLImageElement;
queryImageModalPrevButton = () =>
nativeElement.querySelector(
- '[data-testid="image-modal-prev"]'
+ '[data-testid="image-modal-prev"]',
) as HTMLButtonElement;
queryImageModalNextButton = () =>
nativeElement.querySelector(
- '[data-testid="image-modal-next"]'
+ '[data-testid="image-modal-next"]',
) as HTMLButtonElement;
queryGallery = () =>
nativeElement.querySelector('[data-testid="image-gallery"]');
queryVideos = () =>
Array.from(
- nativeElement.querySelectorAll('[data-testclass="video-attachment"]')
+ nativeElement.querySelectorAll('[data-testclass="video-attachment"]'),
);
queryVideoContainers = () =>
Array.from(
nativeElement.querySelectorAll(
- '[data-testclass="video-attachment-parent"]'
- )
+ '[data-testclass="video-attachment-parent"]',
+ ),
);
queryScrapedVideos = () =>
Array.from(
- nativeElement.querySelectorAll('[data-testclass=scraped-video]')
+ nativeElement.querySelectorAll('[data-testclass=scraped-video]'),
);
}));
@@ -159,25 +159,25 @@ describe('AttachmentListComponent', () => {
expect(attachments.length).toBe(4);
expect(
- attachments[0].classList.contains('str-chat__message-attachment--image')
+ attachments[0].classList.contains('str-chat__message-attachment--image'),
).toBeTrue();
expect(
- attachments[1].classList.contains('str-chat__message-attachment--video')
+ attachments[1].classList.contains('str-chat__message-attachment--video'),
).toBeTrue();
expect(
attachments[2].classList.contains(
- 'str-chat__message-attachment--voice-recording'
- )
+ 'str-chat__message-attachment--voice-recording',
+ ),
).toBeTrue();
expect(
- attachments[3].classList.contains('str-chat__message-attachment--file')
+ attachments[3].classList.contains('str-chat__message-attachment--file'),
).toBeTrue();
expect(
- attachments[3].classList.contains('str-chat__message-attachment--image')
+ attachments[3].classList.contains('str-chat__message-attachment--image'),
).toBeFalse();
expect(queryImages().length).toBe(1);
@@ -186,7 +186,7 @@ describe('AttachmentListComponent', () => {
expect(queryVoiceMessges().length).toBe(1);
expect(queryActions().length).toBe(0);
expect(
- nativeElement.querySelector('.str-chat__message-attachment-with-actions')
+ nativeElement.querySelector('.str-chat__message-attachment-with-actions'),
).toBeNull();
expect(queryVideos().length).toBe(1);
@@ -258,7 +258,7 @@ describe('AttachmentListComponent', () => {
fixture.detectChanges();
const voiceRecordingComponent = fixture.debugElement.query(
- By.directive(VoiceRecordingComponent)
+ By.directive(VoiceRecordingComponent),
).componentInstance as VoiceRecordingComponent;
expect(voiceRecordingComponent.attachment).toBe(mockVoiceRecording);
@@ -294,7 +294,7 @@ describe('AttachmentListComponent', () => {
expect(imageElemnts[0].src).toContain('http://url1');
expect(imageElemnts[1].src).toContain('http://url2');
expect(
- nativeElement.querySelector('.str-chat__gallery-two-rows')
+ nativeElement.querySelector('.str-chat__gallery-two-rows'),
).toBeNull();
component.attachments = [
@@ -314,7 +314,7 @@ describe('AttachmentListComponent', () => {
expect(imageElemnts[2].src).toContain('http://url3');
expect(imageElemnts[3].src).toContain('http://url4');
expect(
- nativeElement.querySelector('.str-chat__gallery-two-rows')
+ nativeElement.querySelector('.str-chat__gallery-two-rows'),
).not.toBeNull();
component.attachments = [
@@ -329,12 +329,12 @@ describe('AttachmentListComponent', () => {
gallery = queryAttachments()[0];
imageElemnts = gallery.querySelectorAll('img');
const lastImage = gallery.querySelector(
- '.str-chat__gallery-placeholder'
+ '.str-chat__gallery-placeholder',
) as HTMLElement;
expect(gallery.querySelectorAll('.str-chat__gallery-image').length).toBe(3);
expect(
- gallery.querySelectorAll('.str-chat__gallery-placeholder').length
+ gallery.querySelectorAll('.str-chat__gallery-placeholder').length,
).toBe(1);
expect(lastImage.style.backgroundImage).toContain('http://url4');
@@ -392,7 +392,7 @@ describe('AttachmentListComponent', () => {
expect(actions[1].innerHTML).toContain('Shuffle');
expect(actions[2].innerHTML).toContain('Cancel');
expect(
- nativeElement.querySelector('.str-chat__message-attachment-with-actions')
+ nativeElement.querySelector('.str-chat__message-attachment-with-actions'),
).not.toBeNull();
});
@@ -450,7 +450,7 @@ describe('AttachmentListComponent', () => {
{
image_action: 'shuffle',
},
- 'parent-id'
+ 'parent-id',
);
});
@@ -512,7 +512,7 @@ describe('AttachmentListComponent', () => {
fixture.detectChanges();
expect(
- nativeElement.querySelector('.str-chat__message-attachment--svg-image')
+ nativeElement.querySelector('.str-chat__message-attachment--svg-image'),
).not.toBeNull();
component.attachments = [
@@ -522,7 +522,7 @@ describe('AttachmentListComponent', () => {
fixture.detectChanges();
expect(
- nativeElement.querySelector('.str-chat__message-attachment--svg-image')
+ nativeElement.querySelector('.str-chat__message-attachment--svg-image'),
).toBeNull();
});
});
@@ -559,14 +559,14 @@ describe('AttachmentListComponent', () => {
expect(
queryAttachments()[0].classList.contains(
- 'str-chat-angular__message-attachment-file-single'
- )
+ 'str-chat-angular__message-attachment-file-single',
+ ),
).toBeTrue();
expect(
queryAttachments()[1].classList.contains(
- 'str-chat-angular__message-attachment-file-single'
- )
+ 'str-chat-angular__message-attachment-file-single',
+ ),
).toBeTrue();
});
@@ -690,7 +690,7 @@ describe('AttachmentListComponent', () => {
expect(
queryAttachments()[0].querySelector('[data-testclass="card-title"]')
- ?.textContent
+ ?.textContent,
).toContain(title);
});
@@ -714,7 +714,7 @@ describe('AttachmentListComponent', () => {
expect(
queryAttachments()[0].querySelector('[data-testclass="card-text"]')
- ?.textContent
+ ?.textContent,
).toContain(text);
});
@@ -758,7 +758,7 @@ describe('AttachmentListComponent', () => {
expect(queryUrlLinks()[0].href).toContain(scrapeUrl);
expect(queryUrlLinks()[0].textContent).toContain(
- component.trimUrl('getstream.io')
+ component.trimUrl('getstream.io'),
);
});
@@ -806,7 +806,7 @@ describe('AttachmentListComponent', () => {
queryGallery()
?.querySelectorAll
(
- '[data-testclass="gallery-image"]'
+ '[data-testclass="gallery-image"]',
)[0]
.click();
fixture.detectChanges();
@@ -835,7 +835,7 @@ describe('AttachmentListComponent', () => {
queryGallery()
?.querySelectorAll(
- '[data-testclass="gallery-image"]'
+ '[data-testclass="gallery-image"]',
)[1]
.click();
fixture.detectChanges();
@@ -866,7 +866,7 @@ describe('AttachmentListComponent', () => {
queryGallery()
?.querySelectorAll(
- '[data-testclass="gallery-image"]'
+ '[data-testclass="gallery-image"]',
)[0]
.click();
fixture.detectChanges();
@@ -945,19 +945,19 @@ describe('AttachmentListComponent', () => {
};
spyOn(
configurationService,
- 'getImageAttachmentConfiguration'
+ 'getImageAttachmentConfiguration',
).and.returnValue(testConfiguration);
spyOn(
configurationService,
- 'getVideoAttachmentConfiguration'
+ 'getVideoAttachmentConfiguration',
).and.returnValue(testConfiguration);
spyOn(
configurationService,
- 'getGiphyAttachmentConfiguration'
+ 'getGiphyAttachmentConfiguration',
).and.returnValue(testConfiguration);
spyOn(
configurationService,
- 'getScrapedImageAttachmentConfiguration'
+ 'getScrapedImageAttachmentConfiguration',
).and.returnValue(testConfiguration);
// Single image, link image, video, giphy
@@ -999,11 +999,11 @@ describe('AttachmentListComponent', () => {
expect(element.style.width).toBe(testConfiguration.width);
expect(
- getComputedStyle(element).getPropertyValue('--original-height')
+ getComputedStyle(element).getPropertyValue('--original-height'),
).toBe(testConfiguration.originalHeight.toString());
expect(
- getComputedStyle(element).getPropertyValue('--original-width')
+ getComputedStyle(element).getPropertyValue('--original-width'),
).toBe(testConfiguration.originalWidth.toString());
});
@@ -1027,18 +1027,18 @@ describe('AttachmentListComponent', () => {
[
...Array.from(gallery.querySelectorAll('img')),
gallery.querySelector(
- '[data-testid="more-image-button"]'
+ '[data-testid="more-image-button"]',
)!,
].forEach((element) => {
expect(element.style.height).toBe(testConfiguration.height);
expect(element.style.width).toBe(testConfiguration.width);
expect(
- getComputedStyle(element).getPropertyValue('--original-height')
+ getComputedStyle(element).getPropertyValue('--original-height'),
).toBe(testConfiguration.originalHeight.toString());
expect(
- getComputedStyle(element).getPropertyValue('--original-width')
+ getComputedStyle(element).getPropertyValue('--original-width'),
).toBe(testConfiguration.originalWidth.toString());
});
@@ -1058,11 +1058,11 @@ describe('AttachmentListComponent', () => {
expect(modalImage.style.width).toBe(testConfiguration.width);
expect(
- getComputedStyle(modalImage).getPropertyValue('--original-height')
+ getComputedStyle(modalImage).getPropertyValue('--original-height'),
).toBe(testConfiguration.originalHeight.toString());
expect(
- getComputedStyle(modalImage).getPropertyValue('--original-width')
+ getComputedStyle(modalImage).getPropertyValue('--original-width'),
).toBe(testConfiguration.originalWidth.toString());
});
@@ -1114,7 +1114,7 @@ describe('AttachmentListComponent with custom attachments', () => {
ngAfterViewInit(): void {
this.customTemplatesService.customAttachmentListTemplate$.next(
- this.template
+ this.template,
);
}
}
@@ -1132,7 +1132,7 @@ describe('AttachmentListComponent with custom attachments', () => {
}).compileComponents();
hostFixture = TestBed.createComponent(
- TestHostComponentAttachmentListComponent
+ TestHostComponentAttachmentListComponent,
);
hostComponent = hostFixture.componentInstance;
hostFixture.detectChanges();
@@ -1140,7 +1140,7 @@ describe('AttachmentListComponent with custom attachments', () => {
it('should display custom attachments', () => {
expect(
- hostFixture.nativeElement.querySelectorAll('.payment-link').length
+ hostFixture.nativeElement.querySelectorAll('.payment-link').length,
).toBe(0);
const customAttachment = {
@@ -1153,7 +1153,7 @@ describe('AttachmentListComponent with custom attachments', () => {
hostFixture.detectChanges();
expect(
- hostFixture.nativeElement.querySelectorAll('.payment-link').length
+ hostFixture.nativeElement.querySelectorAll('.payment-link').length,
).toBe(1);
});
@@ -1171,13 +1171,13 @@ describe('AttachmentListComponent with custom attachments', () => {
hostFixture.detectChanges();
expect(
- hostFixture.nativeElement.querySelector('.str-chat__attachment-list')
+ hostFixture.nativeElement.querySelector('.str-chat__attachment-list'),
).toBeNull();
});
it(`shouldn't display attachments if there are no attachments`, () => {
expect(
- hostFixture.nativeElement.querySelector('.str-chat__attachment-list')
+ hostFixture.nativeElement.querySelector('.str-chat__attachment-list'),
).toBeNull();
});
});
diff --git a/projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts b/projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts
index 85becae4..34dd3fa6 100644
--- a/projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts
+++ b/projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts
@@ -76,17 +76,9 @@ export class AttachmentListComponent implements OnChanges, OnInit, OnDestroy {
public readonly customTemplatesService: CustomTemplatesService,
private channelService: ChannelService,
private attachmentConfigurationService: AttachmentConfigurationService,
- private messageService: MessageService
+ private messageService: MessageService,
) {}
- ngOnInit(): void {
- this.subscriptions.push(
- this.customTemplatesService.customAttachmentListTemplate$.subscribe(
- (t) => (this.customAttachmentsTemplate = t)
- )
- );
- }
-
ngOnChanges(changes: SimpleChanges): void {
if (changes.attachments) {
const builtInAttachments: Attachment[] = [];
@@ -111,24 +103,23 @@ export class AttachmentListComponent implements OnChanges, OnInit, OnDestroy {
// Giphy-s always sent without other attachments
if (this.orderedAttachments.length === 0) {
this.orderedAttachments.push(
- ...builtInAttachments.filter((a) => this.isCard(a))
+ ...builtInAttachments.filter((a) => this.isCard(a)),
);
}
this.customAttachments = customAttachments;
}
}
- ngOnDestroy(): void {
- this.subscriptions.forEach((s) => s.unsubscribe());
+ ngOnInit(): void {
+ this.subscriptions.push(
+ this.customTemplatesService.customAttachmentListTemplate$.subscribe(
+ (t) => (this.customAttachmentsTemplate = t),
+ ),
+ );
}
- trackByUrl(_: number, attachment: Attachment) {
- return (
- attachment.image_url ||
- attachment.img_url ||
- attachment.asset_url ||
- attachment.thumb_url
- );
+ ngOnDestroy(): void {
+ this.subscriptions.forEach((s) => s.unsubscribe());
}
isImage(attachment: Attachment) {
@@ -204,14 +195,10 @@ export class AttachmentListComponent implements OnChanges, OnInit, OnDestroy {
{
[action.name!]: action.value!,
},
- this.parentMessageId
+ this.parentMessageId,
);
}
- trackByActionValue(_: number, item: Action) {
- return item.value;
- }
-
openImageModal(attachments: Attachment[], selectedIndex = 0) {
this.imageModalStateChange.next('opened');
this.imagesToView = attachments;
@@ -222,12 +209,8 @@ export class AttachmentListComponent implements OnChanges, OnInit, OnDestroy {
this.imagesToViewCurrentIndex += dir * 1;
}
- trackByImageUrl(_: number, item: Attachment) {
- return item.image_url || item.img_url || item.thumb_url;
- }
-
getAttachmentContext(
- attachment: Attachment
+ attachment: Attachment,
): AttachmentContext {
return { attachment };
}
@@ -235,7 +218,7 @@ export class AttachmentListComponent implements OnChanges, OnInit, OnDestroy {
getImageAttachmentConfiguration(
attachment: Attachment,
type: 'gallery' | 'single',
- element: HTMLElement
+ element: HTMLElement,
): ImageAttachmentConfiguration {
const existingConfiguration = this.attachmentConfigurations.get(attachment);
if (existingConfiguration) {
@@ -245,7 +228,7 @@ export class AttachmentListComponent implements OnChanges, OnInit, OnDestroy {
this.attachmentConfigurationService.getImageAttachmentConfiguration(
attachment,
type,
- element
+ element,
);
this.attachmentConfigurations.set(attachment, configuration);
return configuration;
@@ -253,18 +236,18 @@ export class AttachmentListComponent implements OnChanges, OnInit, OnDestroy {
getCarouselImageAttachmentConfiguration(
attachment: Attachment,
- element: HTMLElement
+ element: HTMLElement,
) {
return this.attachmentConfigurationService.getImageAttachmentConfiguration(
attachment,
'carousel',
- element
+ element,
);
}
getVideoAttachmentConfiguration(
attachment: Attachment,
- element: HTMLElement
+ element: HTMLElement,
): VideoAttachmentConfiguration {
const existingConfiguration = this.attachmentConfigurations.get(attachment);
if (existingConfiguration) {
@@ -273,7 +256,7 @@ export class AttachmentListComponent implements OnChanges, OnInit, OnDestroy {
const configuration =
this.attachmentConfigurationService.getVideoAttachmentConfiguration(
attachment,
- element
+ element,
);
this.attachmentConfigurations.set(attachment, configuration);
return configuration;
@@ -286,12 +269,12 @@ export class AttachmentListComponent implements OnChanges, OnInit, OnDestroy {
}
if (attachment.type === 'giphy') {
return this.attachmentConfigurationService.getGiphyAttachmentConfiguration(
- attachment
+ attachment,
);
} else {
const configuration =
this.attachmentConfigurationService.getScrapedImageAttachmentConfiguration(
- attachment
+ attachment,
);
this.attachmentConfigurations.set(attachment, configuration);
return configuration;
diff --git a/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.html b/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.html
index b72923ff..1d8cb93d 100644
--- a/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.html
+++ b/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.html
@@ -1,119 +1,121 @@
- 0 && customAttachmentsPreview)
- "
- class="str-chat__attachment-preview-list"
->
-
-
+}
-
+
-
-
-
+ @if (attachmentUpload.state === "error") {
+
+
+
+ }
diff --git a/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.spec.ts b/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.spec.ts
index a52a47e7..ebd7c5f5 100644
--- a/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.spec.ts
+++ b/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.spec.ts
@@ -35,22 +35,22 @@ describe('AttachmentPreviewListComponent', () => {
queryImagePreviews = () =>
Array.from(
nativeElement.querySelectorAll(
- '[data-testclass="attachment-image-preview"]'
- )
+ '[data-testclass="attachment-image-preview"]',
+ ),
);
queryLoadingIndicators = () =>
Array.from(
- nativeElement.querySelectorAll('[data-testclass="loading-indicator"]')
+ nativeElement.querySelectorAll('[data-testclass="loading-indicator"]'),
);
queryPreviewImages = () =>
Array.from(
- nativeElement.querySelectorAll('[data-testclass="attachment-image"]')
+ nativeElement.querySelectorAll('[data-testclass="attachment-image"]'),
);
queryPreviewFiles = () =>
Array.from(
nativeElement.querySelectorAll(
- '[data-testclass="attachment-file-preview"]'
- )
+ '[data-testclass="attachment-file-preview"]',
+ ),
);
component.attachmentUploads$ = attachmentUploads$;
fixture.detectChanges();
@@ -109,11 +109,11 @@ describe('AttachmentPreviewListComponent', () => {
const previews = queryImagePreviews();
expect(
- previews[0].querySelector('[data-testclass="upload-retry"]')
+ previews[0].querySelector('[data-testclass="upload-retry"]'),
).toBeNull();
expect(
- previews[1].querySelector('[data-testclass="upload-retry"]')
+ previews[1].querySelector('[data-testclass="upload-retry"]'),
).not.toBeNull();
});
@@ -149,7 +149,7 @@ describe('AttachmentPreviewListComponent', () => {
filePreviews.forEach((p) => {
expect(
- p.querySelector('.rfu-file-previewer__file--uploading')
+ p.querySelector('.rfu-file-previewer__file--uploading'),
).toBeNull();
expect(p.innerHTML).toContain(url);
expect(p.innerHTML).toContain(fileName);
@@ -174,11 +174,11 @@ describe('AttachmentPreviewListComponent', () => {
const filePreviews = queryPreviewFiles();
expect(
- filePreviews[0].querySelector('[data-testclass="upload-retry"]')
+ filePreviews[0].querySelector('[data-testclass="upload-retry"]'),
).toBeNull();
expect(
- filePreviews[1].querySelector('[data-testclass="upload-retry"]')
+ filePreviews[1].querySelector('[data-testclass="upload-retry"]'),
).not.toBeNull();
});
@@ -212,7 +212,7 @@ describe('AttachmentPreviewListComponent', () => {
component.retryAttachmentUpload.subscribe(spy);
const filePreviews = queryPreviewFiles();
const retryButton = filePreviews[0].querySelector(
- '[data-testclass="upload-retry"]'
+ '[data-testclass="upload-retry"]',
) as HTMLButtonElement;
retryButton.click();
fixture.detectChanges();
@@ -231,7 +231,7 @@ describe('AttachmentPreviewListComponent', () => {
fixture.detectChanges();
const filePreviews = queryPreviewFiles();
const deleteButton = filePreviews[0].querySelector(
- '[data-testclass="file-delete"]'
+ '[data-testclass="file-delete"]',
) as HTMLButtonElement;
const spy = jasmine.createSpy();
component.deleteAttachment.subscribe(spy);
@@ -246,7 +246,7 @@ describe('AttachmentPreviewListComponent', () => {
attachmentUploads$.next([{ file, state: 'error', type: 'image' }]);
fixture.detectChanges();
const retryButton = queryImagePreviews()[0].querySelector(
- '[data-testclass="upload-retry"]'
+ '[data-testclass="upload-retry"]',
) as HTMLButtonElement;
retryButton.click();
attachmentUploads$.next([
@@ -255,7 +255,7 @@ describe('AttachmentPreviewListComponent', () => {
fixture.detectChanges();
expect(
- queryImagePreviews()[0].querySelector('[data-testclass="upload-retry"]')
+ queryImagePreviews()[0].querySelector('[data-testclass="upload-retry"]'),
).toBeNull();
});
@@ -269,7 +269,7 @@ describe('AttachmentPreviewListComponent', () => {
attachmentUploads$.next([upload]);
fixture.detectChanges();
const link = queryPreviewFiles()[0].querySelector(
- '[data-testclass="file-download-link"]'
+ '[data-testclass="file-download-link"]',
) as HTMLAnchorElement;
const event = new KeyboardEvent('click');
spyOn(event, 'preventDefault');
@@ -289,7 +289,7 @@ describe('AttachmentPreviewListComponent', () => {
attachmentUploads$.next([upload]);
fixture.detectChanges();
const link = queryPreviewFiles()[0].querySelector(
- '[data-testclass="file-download-link"]'
+ '[data-testclass="file-download-link"]',
) as HTMLAnchorElement;
expect(link).toBeNull();
@@ -334,7 +334,7 @@ describe('AttachmentPreviewListComponent with custom attachments', () => {
ngAfterViewInit(): void {
this.customTemplatesService.customAttachmentPreviewListTemplate$.next(
- this.template
+ this.template,
);
}
}
@@ -353,7 +353,7 @@ describe('AttachmentPreviewListComponent with custom attachments', () => {
it('should display custom attachments', () => {
expect(
- hostFixture.nativeElement.querySelectorAll('.payment-link').length
+ hostFixture.nativeElement.querySelectorAll('.payment-link').length,
).toBe(0);
const customAttachment = {
@@ -367,7 +367,7 @@ describe('AttachmentPreviewListComponent with custom attachments', () => {
hostFixture.detectChanges();
expect(
- hostFixture.nativeElement.querySelectorAll('.payment-link').length
+ hostFixture.nativeElement.querySelectorAll('.payment-link').length,
).toBe(1);
});
@@ -387,16 +387,16 @@ describe('AttachmentPreviewListComponent with custom attachments', () => {
expect(
hostFixture.nativeElement.querySelector(
- '.str-chat__attachment-preview-list'
- )
+ '.str-chat__attachment-preview-list',
+ ),
).toBeNull();
});
it(`shouldn't display attachments if there are no attachments`, () => {
expect(
hostFixture.nativeElement.querySelector(
- '.str-chat__attachment-preview-list'
- )
+ '.str-chat__attachment-preview-list',
+ ),
).toBeNull();
});
});
diff --git a/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.ts b/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.ts
index da6757e0..7c9d1c51 100644
--- a/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.ts
+++ b/projects/stream-chat-angular/src/lib/attachment-preview-list/attachment-preview-list.component.ts
@@ -40,19 +40,19 @@ export class AttachmentPreviewListComponent implements OnInit, OnDestroy {
constructor(
private customTemplateService: CustomTemplatesService,
- public readonly attachmentService: AttachmentService
+ public readonly attachmentService: AttachmentService,
) {}
ngOnInit(): void {
this.subscriptions.push(
this.customTemplateService.customAttachmentPreviewListTemplate$.subscribe(
- (t) => (this.customAttachmentsPreview = t)
- )
+ (t) => (this.customAttachmentsPreview = t),
+ ),
);
this.subscriptions.push(
this.attachmentService.customAttachments$.subscribe(
- (a) => (this.customAttachments = a)
- )
+ (a) => (this.customAttachments = a),
+ ),
);
}
diff --git a/projects/stream-chat-angular/src/lib/attachment.service.spec.ts b/projects/stream-chat-angular/src/lib/attachment.service.spec.ts
index 38f2a070..fea132bb 100644
--- a/projects/stream-chat-angular/src/lib/attachment.service.spec.ts
+++ b/projects/stream-chat-angular/src/lib/attachment.service.spec.ts
@@ -68,7 +68,7 @@ describe('AttachmentService', () => {
it('should delete attachment, if file is uploading', fakeAsync(() => {
const file = { name: 'myimage.jpg', type: 'image/jpg' } as any as File;
- let resolver!: Function;
+ let resolver!: (value?: unknown) => any;
uploadAttachmentsSpy.and.returnValue(
new Promise((resolve) => {
resolver = () =>
@@ -80,7 +80,7 @@ describe('AttachmentService', () => {
type: 'image' as const,
},
]);
- })
+ }),
);
const attachmentUploadsSpy = jasmine.createSpy('attachmentUploadsSpy');
service.attachmentUploads$.subscribe(attachmentUploadsSpy);
@@ -191,14 +191,14 @@ describe('AttachmentService', () => {
'streamChat.Error uploading file, extension not supported',
'error',
undefined,
- { name: image.name, ext: '.jpg' }
+ { name: image.name, ext: '.jpg' },
);
expect(notificationService.addTemporaryNotification).toHaveBeenCalledWith(
'streamChat.Error uploading file, maximum file size exceeded',
'error',
undefined,
- { name: file.name, limit: '50MB' }
+ { name: file.name, limit: '50MB' },
);
});
@@ -220,7 +220,7 @@ describe('AttachmentService', () => {
expect(notificationService.addPermanentNotification).toHaveBeenCalledWith(
'streamChat.You currently have {{count}} attachments, the maximum is {{max}}',
'error',
- { count: 31, max: service.maxNumberOfAttachments }
+ { count: 31, max: service.maxNumberOfAttachments },
);
});
@@ -236,14 +236,14 @@ describe('AttachmentService', () => {
const files = new Array(3)
.fill(null)
- .map(() => ({ name: 'my_image.png', type: 'image/png' } as File));
+ .map(() => ({ name: 'my_image.png', type: 'image/png' }) as File);
await service.filesSelected(files);
expect(notificationService.addTemporaryNotification).toHaveBeenCalledWith(
`streamChat.You can't uplod more than {{max}} attachments`,
'error',
undefined,
- { max: service.maxNumberOfAttachments }
+ { max: service.maxNumberOfAttachments },
);
expect(uploadAttachmentsSpy).not.toHaveBeenCalled();
@@ -290,7 +290,7 @@ describe('AttachmentService', () => {
`streamChat.You can't uplod more than {{max}} attachments`,
'error',
undefined,
- { max: service.maxNumberOfAttachments }
+ { max: service.maxNumberOfAttachments },
);
expect(uploadAttachmentsSpy).not.toHaveBeenCalled();
@@ -382,7 +382,7 @@ describe('AttachmentService', () => {
{ type: 'video/x-msvideo' },
];
const files = [...imageFiles, ...dataFiles, ...videoFiles];
- let resolver!: Function;
+ let resolver!: (value?: unknown) => any;
uploadAttachmentsSpy.and.returnValue(
new Promise((resolve) => {
resolver = () =>
@@ -414,7 +414,7 @@ describe('AttachmentService', () => {
type: 'video',
},
]);
- })
+ }),
);
void service.filesSelected(files as any as FileList);
tick();
@@ -446,11 +446,11 @@ describe('AttachmentService', () => {
it('should emit the number of uploads in progress', fakeAsync(() => {
const spy = jasmine.createSpy();
service.attachmentUploadInProgressCounter$.subscribe(spy);
- let resolver!: Function;
+ let resolver!: (value?: unknown) => any;
uploadAttachmentsSpy.and.returnValue(
new Promise((resovle) => {
resolver = () => resovle([{}, {}]);
- })
+ }),
);
expect(spy).toHaveBeenCalledWith(0);
@@ -578,7 +578,7 @@ describe('AttachmentService', () => {
const messageService = TestBed.inject(MessageService);
const filterSpy = spyOn(
messageService,
- 'isCustomAttachment'
+ 'isCustomAttachment',
).and.callThrough();
const customAttachment = {
type: 'custom',
@@ -716,7 +716,7 @@ describe('AttachmentService', () => {
const notificationService = TestBed.inject(NotificationService);
const errorNotificationSpy = spyOn(
notificationService,
- 'addTemporaryNotification'
+ 'addTemporaryNotification',
);
appSettings$.next({
file_upload_config: {
@@ -766,7 +766,7 @@ describe('AttachmentService', () => {
const notificationService = TestBed.inject(NotificationService);
const errorNotificationSpy = spyOn(
notificationService,
- 'addTemporaryNotification'
+ 'addTemporaryNotification',
);
appSettings$.next({
file_upload_config: {
@@ -834,7 +834,7 @@ describe('AttachmentService', () => {
expect(result).toBeFalse();
expect(notificationService.addTemporaryNotification).toHaveBeenCalledTimes(
- 2
+ 2,
);
});
diff --git a/projects/stream-chat-angular/src/lib/attachment.service.ts b/projects/stream-chat-angular/src/lib/attachment.service.ts
index a4ce4e6a..0ec98bef 100644
--- a/projects/stream-chat-angular/src/lib/attachment.service.ts
+++ b/projects/stream-chat-angular/src/lib/attachment.service.ts
@@ -29,7 +29,7 @@ import { MessageService } from './message.service';
providedIn: 'root',
})
export class AttachmentService<
- T extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
+ T extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
> {
/**
* Emits the number of uploads in progress.
@@ -60,7 +60,7 @@ export class AttachmentService<
*/
maxNumberOfAttachments = 30;
private attachmentUploadsSubject = new BehaviorSubject(
- []
+ [],
);
private appSettings: AppSettings | undefined;
private attachmentLimitNotificationHide?: () => void;
@@ -69,11 +69,11 @@ export class AttachmentService<
private channelService: ChannelService,
private notificationService: NotificationService,
private chatClientService: ChatClientService,
- private messageService: MessageService
+ private messageService: MessageService,
) {
this.attachmentUploads$ = this.attachmentUploadsSubject.asObservable();
this.chatClientService.appSettings$.subscribe(
- (appSettings) => (this.appSettings = appSettings)
+ (appSettings) => (this.appSettings = appSettings),
);
this.attachmentsCounter$ = combineLatest([
this.attachmentUploads$,
@@ -85,7 +85,7 @@ export class AttachmentService<
customAttachments.length
);
}),
- shareReplay(1)
+ shareReplay(1),
);
this.attachmentsCounter$.subscribe((count) => {
if (count > this.maxNumberOfAttachments) {
@@ -93,7 +93,7 @@ export class AttachmentService<
this.notificationService.addPermanentNotification(
'streamChat.You currently have {{count}} attachments, the maximum is {{max}}',
'error',
- { count, max: this.maxNumberOfAttachments }
+ { count, max: this.maxNumberOfAttachments },
);
} else {
this.attachmentLimitNotificationHide?.();
@@ -253,7 +253,7 @@ export class AttachmentService<
} catch (error) {
result = attachmentUploads;
this.notificationService.addTemporaryNotification(
- 'streamChat.Error deleting attachment'
+ 'streamChat.Error deleting attachment',
);
}
} else {
@@ -385,14 +385,14 @@ export class AttachmentService<
this.chatClientService?.chatClient?.logger(
'error',
e instanceof Error ? e.message : `Can't create image preview`,
- { error: e, tag: ['AttachmentService'] }
+ { error: e, tag: ['AttachmentService'] },
);
}
}
private async uploadAttachments(uploads: AttachmentUpload[]) {
this.attachmentUploadInProgressCounter$.next(
- this.attachmentUploadInProgressCounter$.value + 1
+ this.attachmentUploadInProgressCounter$.value + 1,
);
const result = await this.channelService.uploadAttachments(uploads);
const attachmentUploads = this.attachmentUploadsSubject.getValue();
@@ -431,12 +431,12 @@ export class AttachmentService<
errorKey,
'error',
undefined,
- translateParams
+ translateParams,
);
}
});
this.attachmentUploadInProgressCounter$.next(
- this.attachmentUploadInProgressCounter$.value - 1
+ this.attachmentUploadInProgressCounter$.value - 1,
);
this.attachmentUploadsSubject.next([...attachmentUploads]);
}
@@ -458,42 +458,42 @@ export class AttachmentService<
if (isImageFile(f)) {
hasBlockedExtension =
!!this.appSettings?.image_upload_config?.blocked_file_extensions?.find(
- (ext) => f.name.endsWith(ext)
+ (ext) => f.name.endsWith(ext),
);
hasBlockedMimeType =
!!this.appSettings?.image_upload_config?.blocked_mime_types?.find(
- (type) => f.type === type
+ (type) => f.type === type,
);
hasNotAllowedExtension =
!!this.appSettings?.image_upload_config?.allowed_file_extensions
?.length &&
!this.appSettings?.image_upload_config?.allowed_file_extensions?.find(
- (ext) => f.name.endsWith(ext)
+ (ext) => f.name.endsWith(ext),
);
hasNotAllowedMimeType =
!!this.appSettings?.image_upload_config?.allowed_mime_types?.length &&
!this.appSettings?.image_upload_config?.allowed_mime_types?.find(
- (type) => f.type === type
+ (type) => f.type === type,
);
} else {
hasBlockedExtension =
!!this.appSettings?.file_upload_config?.blocked_file_extensions?.find(
- (ext) => f.name.endsWith(ext)
+ (ext) => f.name.endsWith(ext),
);
hasBlockedMimeType =
!!this.appSettings?.file_upload_config?.blocked_mime_types?.find(
- (type) => f.type === type
+ (type) => f.type === type,
);
hasNotAllowedExtension =
!!this.appSettings?.file_upload_config?.allowed_file_extensions
?.length &&
!this.appSettings?.file_upload_config?.allowed_file_extensions?.find(
- (ext) => f.name.endsWith(ext)
+ (ext) => f.name.endsWith(ext),
);
hasNotAllowedMimeType =
!!this.appSettings?.file_upload_config?.allowed_mime_types?.length &&
!this.appSettings?.file_upload_config?.allowed_mime_types?.find(
- (type) => f.type === type
+ (type) => f.type === type,
);
}
if (
@@ -506,7 +506,7 @@ export class AttachmentService<
'streamChat.Error uploading file, extension not supported',
undefined,
undefined,
- { name: f.name, ext: f.type }
+ { name: f.name, ext: f.type },
);
isValid = false;
}
@@ -544,7 +544,7 @@ export class AttachmentService<
'streamChat.Error uploading file, maximum file size exceeded',
undefined,
undefined,
- { name: f.name, limit: limit }
+ { name: f.name, limit: limit },
);
isValid = false;
}
@@ -565,7 +565,7 @@ export class AttachmentService<
`streamChat.You can't uplod more than {{max}} attachments`,
'error',
undefined,
- { max: this.maxNumberOfAttachments }
+ { max: this.maxNumberOfAttachments },
);
return false;
} else {
diff --git a/projects/stream-chat-angular/src/lib/avatar-placeholder/avatar-placeholder.component.html b/projects/stream-chat-angular/src/lib/avatar-placeholder/avatar-placeholder.component.html
index 194f2438..f9e5cd69 100644
--- a/projects/stream-chat-angular/src/lib/avatar-placeholder/avatar-placeholder.component.html
+++ b/projects/stream-chat-angular/src/lib/avatar-placeholder/avatar-placeholder.component.html
@@ -18,11 +18,11 @@
[location]="location"
[initialsType]="initialsType"
[showOnlineIndicator]="showOnlineIndicator"
- >
+ />
+/>
diff --git a/projects/stream-chat-angular/src/lib/avatar/avatar.component.html b/projects/stream-chat-angular/src/lib/avatar/avatar.component.html
index e78a76cd..3dc62460 100644
--- a/projects/stream-chat-angular/src/lib/avatar/avatar.component.html
+++ b/projects/stream-chat-angular/src/lib/avatar/avatar.component.html
@@ -8,23 +8,24 @@
}}"
title="{{ name }}"
>
-
-
+ @if ((imageUrl || fallbackChannelImage) && !isError) {
+
+ } @else {
{{ initials }}
-
-
+ }
+ @if (isOnline && showOnlineIndicator) {
+
+ }
diff --git a/projects/stream-chat-angular/src/lib/avatar/avatar.component.spec.ts b/projects/stream-chat-angular/src/lib/avatar/avatar.component.spec.ts
index 2555d3e0..251d46a7 100644
--- a/projects/stream-chat-angular/src/lib/avatar/avatar.component.spec.ts
+++ b/projects/stream-chat-angular/src/lib/avatar/avatar.component.spec.ts
@@ -48,7 +48,7 @@ describe('AvatarComponent', () => {
const img = queryImg();
return new Promise((resolve, reject) => {
if (!img) {
- return reject();
+ return reject(new Error('Image not found'));
}
img.addEventListener('load', () => resolve(undefined));
img.addEventListener('error', () => resolve(undefined));
diff --git a/projects/stream-chat-angular/src/lib/avatar/avatar.component.ts b/projects/stream-chat-angular/src/lib/avatar/avatar.component.ts
index 5a63af03..a000af3d 100644
--- a/projects/stream-chat-angular/src/lib/avatar/avatar.component.ts
+++ b/projects/stream-chat-angular/src/lib/avatar/avatar.component.ts
@@ -25,10 +25,10 @@ import {
@Component({
selector: 'stream-avatar',
templateUrl: './avatar.component.html',
- styleUrls: ['./avatar.component.scss'],
+ styleUrl: './avatar.component.scss',
})
export class AvatarComponent
- implements OnChanges, OnInit, OnChanges, AfterViewInit, OnDestroy
+ implements OnChanges, OnInit, AfterViewInit, OnDestroy
{
/**
* An optional name of the image, used for fallback image or image title (if `imageUrl` is provided)
@@ -76,9 +76,22 @@ export class AvatarComponent
constructor(
private chatClientService: ChatClientService,
private ngZone: NgZone,
- private cdRef: ChangeDetectorRef
+ private cdRef: ChangeDetectorRef,
) {}
+ ngOnChanges(changes: SimpleChanges) {
+ if (changes['channel']) {
+ this.updateIsOnlineSubscription();
+ }
+ if (changes.type || changes.name || changes.channel) {
+ this.setInitials();
+ }
+
+ if (changes.type || changes.channel) {
+ this.setFallbackChannelImage();
+ }
+ }
+
ngOnInit(): void {
this.subscriptions.push(
this.chatClientService.user$.subscribe((u) => {
@@ -93,21 +106,12 @@ export class AvatarComponent
this.cdRef.detectChanges();
}
}
- })
+ }),
);
}
- ngOnChanges(changes: SimpleChanges) {
- if (changes['channel']) {
- this.updateIsOnlineSubscription();
- }
- if (changes.type || changes.name || changes.channel) {
- this.setInitials();
- }
-
- if (changes.type || changes.channel) {
- this.setFallbackChannelImage();
- }
+ ngAfterViewInit(): void {
+ this.isViewInited = true;
}
ngOnDestroy(): void {
@@ -176,13 +180,9 @@ export class AvatarComponent
}
}
- ngAfterViewInit(): void {
- this.isViewInited = true;
- }
-
private getOtherMemberIfOneToOneChannel() {
const otherMembers = Object.values(
- this.channel?.state?.members || {}
+ this.channel?.state?.members || {},
).filter((m) => m.user_id !== this.userId);
if (otherMembers.length === 1) {
return otherMembers[0].user;
diff --git a/projects/stream-chat-angular/src/lib/channel-header/channel-header.component.html b/projects/stream-chat-angular/src/lib/channel-header/channel-header.component.html
index cc7cd2e3..1878600f 100644
--- a/projects/stream-chat-angular/src/lib/channel-header/channel-header.component.html
+++ b/projects/stream-chat-angular/src/lib/channel-header/channel-header.component.html
@@ -1,12 +1,12 @@
diff --git a/projects/stream-chat-angular/src/lib/channel-header/channel-header.component.spec.ts b/projects/stream-chat-angular/src/lib/channel-header/channel-header.component.spec.ts
index b6644202..c747672f 100644
--- a/projects/stream-chat-angular/src/lib/channel-header/channel-header.component.spec.ts
+++ b/projects/stream-chat-angular/src/lib/channel-header/channel-header.component.spec.ts
@@ -112,7 +112,7 @@ describe('ChannelHeaderComponent', () => {
expect(avatar.type).toBe('channel');
expect(avatar.location).toBe('channel-header');
expect(avatar.channel).toBe(
- channel as any as Channel