Skip to content

Commit 8b6e15a

Browse files
authored
Merge pull request #400 from GetStream/ios-scroll-issues
Ios scroll issues
2 parents cb79823 + 08422e5 commit 8b6e15a

File tree

8 files changed

+117
-25
lines changed

8 files changed

+117
-25
lines changed

projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.spec.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,9 @@ describe('AttachmentListComponent', () => {
645645
});
646646

647647
it('should open image viewer modal - single image', () => {
648+
const imageModalSpy = jasmine.createSpy();
649+
component.imageModalStateChange.subscribe(imageModalSpy);
650+
648651
const attachment = {
649652
type: 'image',
650653
image_url: 'http://url1',
@@ -663,6 +666,7 @@ describe('AttachmentListComponent', () => {
663666
expect(component.imagesToViewCurrentIndex).toBe(0);
664667
expect(queryImageModalPrevButton()?.style.visibility).toBe('hidden');
665668
expect(queryImageModalNextButton()?.style.visibility).toBe('hidden');
669+
expect(imageModalSpy).toHaveBeenCalledWith('opened');
666670
});
667671

668672
it('should open image viewer modal - image gallery', () => {
@@ -773,19 +777,23 @@ describe('AttachmentListComponent', () => {
773777
expect(queryImageModalNextButton()?.style.visibility).toBe('hidden');
774778
});
775779

776-
// Will be part of modal implementation
777-
// eslint-disable-next-line jasmine/no-disabled-tests
778-
xit('should deselect images if modal is closed', () => {
780+
it('should deselect images if modal is closed', () => {
781+
const imageModalSpy = jasmine.createSpy();
782+
component.imageModalStateChange.subscribe(imageModalSpy);
783+
imageModalSpy.calls.reset();
779784
const attachment = {
780785
type: 'image',
781786
image_url: 'http://url1',
782787
};
788+
component.attachments = [attachment];
789+
component.ngOnChanges({ attachments: {} as SimpleChange });
783790
component.imagesToView = [attachment];
784791
fixture.detectChanges();
785792
queryImageModal().close();
786793
fixture.detectChanges();
787794

788795
expect(component.imagesToView).toEqual([]);
796+
expect(imageModalSpy).toHaveBeenCalledWith('closed');
789797
});
790798
});
791799

projects/stream-chat-angular/src/lib/attachment-list/attachment-list.component.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import {
22
Component,
3+
EventEmitter,
34
HostBinding,
45
Input,
56
OnChanges,
7+
Output,
68
SimpleChanges,
79
TemplateRef,
810
ViewChild,
@@ -43,6 +45,12 @@ export class AttachmentListComponent implements OnChanges {
4345
* The attachments to display
4446
*/
4547
@Input() attachments: Attachment<DefaultStreamChatGenerics>[] = [];
48+
/**
49+
* Emits the state of the image carousel window
50+
*/
51+
@Output() readonly imageModalStateChange = new EventEmitter<
52+
'opened' | 'closed'
53+
>();
4654
@HostBinding() class = 'str-chat__attachment-list-angular-host';
4755
orderedAttachments: Attachment<DefaultStreamChatGenerics>[] = [];
4856
imagesToView: Attachment<DefaultStreamChatGenerics>[] = [];
@@ -172,6 +180,7 @@ export class AttachmentListComponent implements OnChanges {
172180
}
173181

174182
openImageModal(attachments: Attachment[], selectedIndex = 0) {
183+
this.imageModalStateChange.next('opened');
175184
this.imagesToView = attachments;
176185
this.imagesToViewCurrentIndex = selectedIndex;
177186
}
@@ -268,6 +277,7 @@ export class AttachmentListComponent implements OnChanges {
268277
}
269278

270279
private closeImageModal() {
280+
this.imageModalStateChange.next('closed');
271281
this.imagesToView = [];
272282
}
273283
}

projects/stream-chat-angular/src/lib/message-list/message-list.component.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
replies' | translate:replyCountParam)}}
2424
</div>
2525
</li>
26+
<stream-loading-indicator
27+
data-testid="top-loading-indicator"
28+
*ngIf="isLoading && direction === 'bottom-to-top'"
29+
></stream-loading-indicator>
2630
<li
2731
tabindex="0"
2832
data-testclass="message"
@@ -41,6 +45,10 @@
4145
"
4246
></ng-container>
4347
</li>
48+
<stream-loading-indicator
49+
data-testid="bottom-loading-indicator"
50+
*ngIf="isLoading && direction === 'top-to-bottom'"
51+
></stream-loading-indicator>
4452
</ul>
4553
<ng-template #defaultTypingIndicator let-usersTyping$="usersTyping$">
4654
<!-- eslint-disable-next-line @angular-eslint/template/no-any -->

projects/stream-chat-angular/src/lib/message-list/message-list.component.spec.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ describe('MessageListComponent', () => {
4040
let queryParentMessageReplyCount: () => HTMLElement | null;
4141
let queryTypingIndicator: () => HTMLElement | null;
4242
let queryTypingUsers: () => HTMLElement | null;
43+
let queryLoadingIndicator: (pos: 'top' | 'bottom') => HTMLElement | null;
4344

4445
beforeEach(() => {
4546
channelServiceMock = mockChannelService();
@@ -90,6 +91,8 @@ describe('MessageListComponent', () => {
9091
nativeElement.querySelector('[data-testid="typing-users"]');
9192
queryParentMessageReplyCount = () =>
9293
nativeElement.querySelector('[data-testid="reply-count"]');
94+
queryLoadingIndicator = (pos: 'top' | 'bottom') =>
95+
nativeElement.querySelector(`[data-testid="${pos}-loading-indicator"]`);
9396
TestBed.inject(StreamI18nService).setTranslation('en');
9497
fixture.detectChanges();
9598
const scrollContainer = queryScrollContainer()!;
@@ -870,4 +873,41 @@ describe('MessageListComponent', () => {
870873
);
871874
});
872875
});
876+
877+
it('should set isLoading flag', () => {
878+
expect(component.isLoading).toBeFalse();
879+
880+
const scrollContainer = queryScrollContainer()!;
881+
scrollContainer.scrollTo({ top: 0 });
882+
scrollContainer.dispatchEvent(new Event('scroll'));
883+
fixture.detectChanges();
884+
885+
expect(component.isLoading).toBeTrue();
886+
887+
channelServiceMock.activeChannelMessages$.next(generateMockMessages());
888+
889+
expect(component.isLoading).toBeFalse();
890+
});
891+
892+
it('should display loading indicator', () => {
893+
component.isLoading = false;
894+
fixture.detectChanges();
895+
896+
expect(queryLoadingIndicator('top')).toBeNull();
897+
expect(queryLoadingIndicator('bottom')).toBeNull();
898+
899+
component.direction = 'top-to-bottom';
900+
component.isLoading = true;
901+
fixture.detectChanges();
902+
903+
expect(queryLoadingIndicator('top')).toBeNull();
904+
expect(queryLoadingIndicator('bottom')).not.toBeNull();
905+
906+
component.direction = 'bottom-to-top';
907+
component.isLoading = true;
908+
fixture.detectChanges();
909+
910+
expect(queryLoadingIndicator('top')).not.toBeNull();
911+
expect(queryLoadingIndicator('bottom')).toBeNull();
912+
});
873913
});

projects/stream-chat-angular/src/lib/message-list/message-list.component.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export class MessageListComponent
5858
lastSentMessageId: string | undefined;
5959
parentMessage: StreamMessage | undefined;
6060
highlightedMessageId: string | undefined;
61+
isLoading = false;
6162
@ViewChild('scrollContainer')
6263
private scrollContainer!: ElementRef<HTMLElement>;
6364
@ViewChild('parentMessageElement')
@@ -276,6 +277,7 @@ export class MessageListComponent
276277
this.mode === 'main'
277278
? void this.channelService.loadMoreMessages(direction)
278279
: void this.channelService.loadMoreThreadReplies(direction);
280+
this.isLoading = true;
279281
}
280282
this.prevScrollTop = this.scrollContainer.nativeElement.scrollTop;
281283
}
@@ -346,6 +348,7 @@ export class MessageListComponent
346348
: this.channelService.activeThreadMessages$
347349
).pipe(
348350
tap((messages) => {
351+
this.isLoading = false;
349352
if (messages.length === 0) {
350353
return;
351354
}

projects/stream-chat-angular/src/lib/message/message.component.html

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -163,25 +163,19 @@
163163
"
164164
></ng-container>
165165
</div>
166-
<div class="str-chat__message-bubble">
166+
<!-- transform: translate3d(0, 0, 0) fixes scrolling issues on iOS, see: https://stackoverflow.com/questions/50105780/elements-disappear-when-scrolling-in-safari-webkit-transform-fix-only-works-t/50144295#50144295 -->
167+
<!-- transform: none is required when image carousel is open in order for the modal to be correctly positioned, see how the transform property changes the behavior of fixed positioned elements https://developer.mozilla.org/en-US/docs/Web/CSS/position -->
168+
<div
169+
class="str-chat__message-bubble"
170+
style="transform: {{
171+
imageAttachmentModalState === 'opened'
172+
? 'none'
173+
: 'translate3d(0, 0, 0)'
174+
}}"
175+
>
167176
<ng-container *ngIf="hasAttachment && !message?.quoted_message">
168-
<ng-template
169-
#defaultAttachments
170-
let-messageId="messageId"
171-
let-attachments="attachments"
172-
let-parentMessageId="parentMessageId"
173-
>
174-
<stream-attachment-list
175-
[messageId]="messageId"
176-
[attachments]="attachments"
177-
[parentMessageId]="parentMessageId"
178-
></stream-attachment-list>
179-
</ng-template>
180177
<ng-container
181-
*ngTemplateOutlet="
182-
attachmentListTemplate || defaultAttachments;
183-
context: getAttachmentListContext()
184-
"
178+
*ngTemplateOutlet="attachmentsTemplate"
185179
></ng-container>
186180
</ng-container>
187181
<div
@@ -209,11 +203,11 @@
209203
"
210204
>
211205
<ng-container *ngTemplateOutlet="quotedMessage"></ng-container>
212-
<stream-attachment-list
213-
*ngIf="hasAttachment && message?.quoted_message"
214-
[attachments]="message!.attachments!"
215-
[messageId]="message!.id"
216-
></stream-attachment-list>
206+
<ng-container *ngIf="hasAttachment && message?.quoted_message">
207+
<ng-container
208+
*ngTemplateOutlet="attachmentsTemplate"
209+
></ng-container>
210+
</ng-container>
217211
<div
218212
data-testid="client-error-message"
219213
*ngIf="message?.type === 'error'"
@@ -365,11 +359,13 @@
365359
let-messageId="messageId"
366360
let-attachments="attachments"
367361
let-parentMessageId="parentMessageId"
362+
let-imageModalStateChangeHandler="imageModalStateChangeHandler"
368363
>
369364
<stream-attachment-list
370365
[messageId]="messageId"
371366
[attachments]="attachments"
372367
[parentMessageId]="parentMessageId"
368+
(imageModalStateChange)="imageModalStateChangeHandler($event)"
373369
></stream-attachment-list>
374370
</ng-template>
375371
<ng-container
@@ -623,3 +619,26 @@
623619
</button>
624620
</div>
625621
</ng-template>
622+
623+
<ng-template #attachmentsTemplate>
624+
<ng-template
625+
#defaultAttachments
626+
let-messageId="messageId"
627+
let-attachments="attachments"
628+
let-parentMessageId="parentMessageId"
629+
let-imageModalStateChangeHandler="imageModalStateChangeHandler"
630+
>
631+
<stream-attachment-list
632+
[messageId]="messageId"
633+
[attachments]="attachments"
634+
[parentMessageId]="parentMessageId"
635+
(imageModalStateChange)="imageModalStateChangeHandler($event)"
636+
></stream-attachment-list>
637+
</ng-template>
638+
<ng-container
639+
*ngTemplateOutlet="
640+
attachmentListTemplate || defaultAttachments;
641+
context: getAttachmentListContext()
642+
"
643+
></ng-container>
644+
</ng-template>

projects/stream-chat-angular/src/lib/message/message.component.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ export class MessageComponent implements OnInit, OnChanges, OnDestroy {
8989
popperPlacementAuto = NgxPopperjsPlacements.AUTO;
9090
shouldDisplayTranslationNotice = false;
9191
displayedMessageTextContent: 'original' | 'translation' = 'original';
92+
imageAttachmentModalState: 'opened' | 'closed' = 'closed';
9293
private quotedMessageAttachments: Attachment[] | undefined;
9394
private user: UserResponse<DefaultStreamChatGenerics> | undefined;
9495
private subscriptions: Subscription[] = [];
@@ -258,6 +259,8 @@ export class MessageComponent implements OnInit, OnChanges, OnDestroy {
258259
messageId: this.message?.id || '',
259260
attachments: this.message?.attachments || [],
260261
parentMessageId: this.message?.parent_id,
262+
imageModalStateChangeHandler: (state) =>
263+
(this.imageAttachmentModalState = state),
261264
};
262265
}
263266

projects/stream-chat-angular/src/lib/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ export type AttachmentListContext = {
162162
messageId: string;
163163
attachments: Attachment<DefaultStreamChatGenerics>[];
164164
parentMessageId?: string;
165+
imageModalStateChangeHandler?: (state: 'opened' | 'closed') => {};
165166
};
166167

167168
export type AvatarType = 'channel' | 'user';

0 commit comments

Comments
 (0)