Skip to content

Commit 2828b5d

Browse files
committed
Remove getters from avatar and message reactions components
1 parent 6d1f80b commit 2828b5d

File tree

6 files changed

+206
-50
lines changed

6 files changed

+206
-50
lines changed

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

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { SimpleChange } from '@angular/core';
22
import { ComponentFixture, TestBed } from '@angular/core/testing';
3-
import { Subject } from 'rxjs';
3+
import { Observable, Subject, of } from 'rxjs';
44
import { ChatClientService, ClientEvent } from '../chat-client.service';
55
import { generateMockChannels } from '../mocks';
66
import { AvatarComponent } from './avatar.component';
@@ -18,13 +18,15 @@ describe('AvatarComponent', () => {
1818
let chatClientServiceMock: {
1919
chatClient: { user: { id: string } };
2020
events$: Subject<ClientEvent>;
21+
user$: Observable<{ id: string }>;
2122
};
2223

2324
beforeEach(() => {
2425
events$ = new Subject();
2526
chatClientServiceMock = {
2627
chatClient: { user: { id: 'current-user' } },
2728
events$,
29+
user$: of({ id: 'current-user' }),
2830
};
2931
TestBed.configureTestingModule({
3032
declarations: [AvatarComponent],
@@ -91,6 +93,7 @@ describe('AvatarComponent', () => {
9193
it(`should display fallback image if #imageUrl wasn't provided`, () => {
9294
component.name = 'John Doe';
9395
component.type = 'user';
96+
component.ngOnChanges({ type: {} as SimpleChange });
9497
fixture.detectChanges();
9598
const img = queryImg();
9699
const fallbackImg = queryFallbackImg();
@@ -104,16 +107,28 @@ describe('AvatarComponent', () => {
104107
it('should display initials correctly', () => {
105108
component.type = 'user';
106109
component.name = 'John Doe';
110+
component.ngOnChanges({
111+
type: {} as SimpleChange,
112+
name: {} as SimpleChange,
113+
});
107114
fixture.detectChanges();
108115

109116
expect(component.initials).toBe('J');
110117

111118
component.name = 'Johhny';
119+
component.ngOnChanges({
120+
name: {} as SimpleChange,
121+
});
122+
112123
fixture.detectChanges();
113124

114125
expect(component.initials).toBe('J');
115126

116127
component.name = undefined;
128+
component.ngOnChanges({
129+
name: {} as SimpleChange,
130+
});
131+
117132
fixture.detectChanges();
118133

119134
expect(component.initials).toBe('');
@@ -122,6 +137,11 @@ describe('AvatarComponent', () => {
122137
channel.data!.name = undefined;
123138
component.channel = channel;
124139
component.type = 'channel';
140+
component.ngOnChanges({
141+
channel: {} as SimpleChange,
142+
type: {} as SimpleChange,
143+
});
144+
125145
fixture.detectChanges();
126146

127147
expect(component.initials).toBe('#');
@@ -140,6 +160,11 @@ describe('AvatarComponent', () => {
140160
};
141161
component.channel = channel;
142162
component.type = 'channel';
163+
component.ngOnChanges({
164+
type: {} as SimpleChange,
165+
channel: {} as SimpleChange,
166+
});
167+
143168
fixture.detectChanges();
144169

145170
expect(component.initials).toBe('T');
@@ -157,11 +182,18 @@ describe('AvatarComponent', () => {
157182
},
158183
};
159184
component.channel = channel;
185+
component.ngOnChanges({
186+
channel: {} as SimpleChange,
187+
});
188+
160189
fixture.detectChanges();
161190

162191
expect(component.initials).toBe('J');
163192

164193
delete channel.state.members['otheruser'].user!.name;
194+
component.ngOnChanges({
195+
channel: {} as SimpleChange,
196+
});
165197

166198
expect(component.initials).toBe('o');
167199

@@ -176,6 +208,10 @@ describe('AvatarComponent', () => {
176208
},
177209
};
178210
component.channel = channel;
211+
component.ngOnChanges({
212+
channel: {} as SimpleChange,
213+
});
214+
179215
fixture.detectChanges();
180216

181217
expect(component.initials).toBe('#');
@@ -196,6 +232,10 @@ describe('AvatarComponent', () => {
196232
component.imageUrl = undefined;
197233
component.channel = channel;
198234
component.type = 'channel';
235+
component.ngOnChanges({
236+
type: {} as SimpleChange,
237+
channel: {} as SimpleChange,
238+
});
199239
fixture.detectChanges();
200240

201241
expect(queryImg()?.src).toContain('url/to/img');
@@ -207,6 +247,9 @@ describe('AvatarComponent', () => {
207247

208248
channel.state.members.otheruser.user!.image = undefined;
209249
component.imageUrl = undefined;
250+
component.ngOnChanges({
251+
channel: {} as SimpleChange,
252+
});
210253
fixture.detectChanges();
211254

212255
expect(queryImg()).toBeNull();
@@ -215,6 +258,9 @@ describe('AvatarComponent', () => {
215258
user_id: 'thirduser',
216259
user: { id: 'thirduser', image: 'profile/img' },
217260
};
261+
component.ngOnChanges({
262+
channel: {} as SimpleChange,
263+
});
218264
fixture.detectChanges();
219265

220266
expect(queryImg()).toBeNull();
@@ -370,6 +416,10 @@ describe('AvatarComponent', () => {
370416
component.type = 'user';
371417
component.initialsType = 'first-letter-of-each-word';
372418
component.name = 'John Doe';
419+
component.ngOnChanges({
420+
name: {} as SimpleChange,
421+
type: {} as SimpleChange,
422+
});
373423
fixture.detectChanges();
374424

375425
expect(component.initials).toBe('JD');

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

Lines changed: 73 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import {
2+
AfterViewInit,
3+
ChangeDetectorRef,
24
Component,
35
Input,
46
NgZone,
57
OnChanges,
8+
OnInit,
69
SimpleChanges,
710
} from '@angular/core';
811
import { Subscription } from 'rxjs';
@@ -23,7 +26,9 @@ import {
2326
templateUrl: './avatar.component.html',
2427
styleUrls: ['./avatar.component.scss'],
2528
})
26-
export class AvatarComponent implements OnChanges {
29+
export class AvatarComponent
30+
implements OnChanges, OnInit, OnChanges, AfterViewInit
31+
{
2732
/**
2833
* An optional name of the image, used for fallback image or image title (if `imageUrl` is provided)
2934
*/
@@ -65,38 +70,63 @@ export class AvatarComponent implements OnChanges {
6570
isError = false;
6671
isOnline = false;
6772
private isOnlineSubscription?: Subscription;
73+
initials: string = '';
74+
fallbackChannelImage: string | undefined;
75+
private userId?: string;
76+
private isViewInited = false;
77+
private subscriptions: Subscription[] = [];
6878

6979
constructor(
7080
private chatClientService: ChatClientService,
71-
private ngZone: NgZone
81+
private ngZone: NgZone,
82+
private cdRef: ChangeDetectorRef
7283
) {}
7384

85+
ngOnInit(): void {
86+
this.subscriptions.push(
87+
this.chatClientService.user$.subscribe((u) => {
88+
if (u?.id !== this.userId) {
89+
this.userId = u?.id;
90+
if (this.type || this.channel || this.name) {
91+
this.setInitials();
92+
this.setFallbackChannelImage();
93+
this.updateIsOnlineSubscription();
94+
}
95+
if (this.isViewInited) {
96+
this.cdRef.detectChanges();
97+
}
98+
}
99+
})
100+
);
101+
}
102+
74103
ngOnChanges(changes: SimpleChanges) {
75104
if (changes['channel']) {
76-
if (this.channel) {
77-
const otherMember = this.getOtherMemberIfOneToOneChannel();
78-
if (otherMember) {
79-
this.isOnline = otherMember.online || false;
80-
this.isOnlineSubscription = this.chatClientService.events$
81-
.pipe(filter((e) => e.eventType === 'user.presence.changed'))
82-
.subscribe((event) => {
83-
if (event.event.user?.id === otherMember.id) {
84-
this.ngZone.run(() => {
85-
this.isOnline = event.event.user?.online || false;
86-
});
87-
}
88-
});
89-
} else {
90-
this.isOnlineSubscription?.unsubscribe();
91-
}
105+
this.updateIsOnlineSubscription();
106+
}
107+
if (changes.type || changes.name || changes.channel) {
108+
this.setInitials();
109+
}
110+
111+
if (changes.type || changes.channel) {
112+
this.setFallbackChannelImage();
113+
}
114+
}
115+
116+
private setFallbackChannelImage() {
117+
if (this.type !== 'channel') {
118+
this.fallbackChannelImage = undefined;
119+
} else {
120+
const otherMember = this.getOtherMemberIfOneToOneChannel();
121+
if (otherMember) {
122+
this.fallbackChannelImage = otherMember.image;
92123
} else {
93-
this.isOnline = false;
94-
this.isOnlineSubscription?.unsubscribe();
124+
this.fallbackChannelImage = undefined;
95125
}
96126
}
97127
}
98128

99-
get initials() {
129+
private setInitials() {
100130
let result: string = '';
101131
if (this.type === 'user') {
102132
result = this.name?.toString() || '';
@@ -120,26 +150,40 @@ export class AvatarComponent implements OnChanges {
120150
} else {
121151
initials = words[0].charAt(0) || '';
122152
}
123-
return initials;
153+
this.initials = initials;
124154
}
125155

126-
get fallbackChannelImage() {
127-
if (this.type !== 'channel') {
128-
return undefined;
129-
} else {
156+
private updateIsOnlineSubscription() {
157+
if (this.channel) {
130158
const otherMember = this.getOtherMemberIfOneToOneChannel();
131159
if (otherMember) {
132-
return otherMember.image;
160+
this.isOnline = otherMember.online || false;
161+
this.isOnlineSubscription = this.chatClientService.events$
162+
.pipe(filter((e) => e.eventType === 'user.presence.changed'))
163+
.subscribe((event) => {
164+
if (event.event.user?.id === otherMember.id) {
165+
this.ngZone.run(() => {
166+
this.isOnline = event.event.user?.online || false;
167+
});
168+
}
169+
});
133170
} else {
134-
return undefined;
171+
this.isOnlineSubscription?.unsubscribe();
135172
}
173+
} else {
174+
this.isOnline = false;
175+
this.isOnlineSubscription?.unsubscribe();
136176
}
137177
}
138178

179+
ngAfterViewInit(): void {
180+
this.isViewInited = true;
181+
}
182+
139183
private getOtherMemberIfOneToOneChannel() {
140184
const otherMembers = Object.values(
141185
this.channel?.state?.members || {}
142-
).filter((m) => m.user_id !== this.chatClientService.chatClient.user?.id);
186+
).filter((m) => m.user_id !== this.userId);
143187
if (otherMembers.length === 1) {
144188
return otherMembers[0].user;
145189
} else {

projects/stream-chat-angular/src/lib/channel-preview/channel-preview.component.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
mockMessage,
1414
} from '../mocks';
1515
import { ChannelPreviewComponent } from './channel-preview.component';
16+
import { Observable, of } from 'rxjs';
1617

1718
describe('ChannelPreviewComponent', () => {
1819
let fixture: ComponentFixture<ChannelPreviewComponent>;
@@ -21,6 +22,7 @@ describe('ChannelPreviewComponent', () => {
2122
let channelServiceMock: MockChannelService;
2223
let chatClientServiceMock: {
2324
chatClient: { user: UserResponse };
25+
user$: Observable<{ id: string }>;
2426
};
2527
let queryContainer: () => HTMLElement | null;
2628
let queryAvatar: () => AvatarPlaceholderComponent;
@@ -32,6 +34,7 @@ describe('ChannelPreviewComponent', () => {
3234
channelServiceMock = mockChannelService();
3335
chatClientServiceMock = {
3436
chatClient: { user: { id: 'currentUser' } },
37+
user$: of({ id: 'currentUser' }),
3538
};
3639
TestBed.configureTestingModule({
3740
imports: [TranslateModule.forRoot()],

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
} from '@angular/core/testing';
1010
import { By } from '@angular/platform-browser';
1111
import { TranslateModule } from '@ngx-translate/core';
12-
import { BehaviorSubject, Subject } from 'rxjs';
12+
import { BehaviorSubject, Subject, of } from 'rxjs';
1313
import { AppSettings, Channel, UserResponse } from 'stream-chat';
1414
import { AttachmentService } from '../attachment.service';
1515
import { ChannelService } from '../channel.service';
@@ -138,7 +138,12 @@ describe('MessageInputComponent', () => {
138138
},
139139
{
140140
provide: ChatClientService,
141-
useValue: { chatClient: { user }, appSettings$, getAppSettings },
141+
useValue: {
142+
user$: of(user),
143+
chatClient: { user },
144+
appSettings$,
145+
getAppSettings,
146+
},
142147
},
143148
{
144149
provide: ThemeService,

0 commit comments

Comments
 (0)