Skip to content

Commit e08ffb1

Browse files
fix: avatar update not reflecting in ProfileView and SidebarView (#7430)
* fix: dispatch avatar etag * unit test
1 parent 3e15245 commit e08ffb1

2 files changed

Lines changed: 99 additions & 0 deletions

File tree

app/lib/services/connect.test.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { connect, determineAuthType, disconnect } from './connect';
22
import { mediaSessionInstance } from './voip/MediaSessionInstance';
33
import { pendingHangups } from './voip/pendingHangups';
44
import { unsubscribeRooms } from '../methods/subscribeRooms';
5+
import { setUser } from '../../actions/login';
6+
import database from '../database';
57

68
jest.mock('./voip/MediaSessionInstance', () => ({
79
mediaSessionInstance: { reset: jest.fn(), drainPendingHangups: jest.fn() }
@@ -512,4 +514,92 @@ describe('connect — rooms subscription guard reset on close', () => {
512514
});
513515
});
514516

517+
describe('connect — stream-notify-logged updateAvatar', () => {
518+
const mockUserUpdate = jest.fn<Promise<void>, [(u: { avatarETag: string }) => void]>(fn => {
519+
fn({ avatarETag: '' });
520+
return Promise.resolve();
521+
});
522+
const mockUserRecord = { update: (fn: (u: { avatarETag: string }) => void) => mockUserUpdate(fn) };
523+
const mockFetch = jest.fn<Promise<unknown[]>, []>(() => Promise.resolve([mockUserRecord]));
524+
const mockDbWrite = jest.fn<Promise<void>, [() => Promise<void>]>(async fn => {
525+
await fn();
526+
});
527+
528+
beforeEach(async () => {
529+
jest.clearAllMocks();
530+
mockOnStreamDataStops.length = 0;
531+
mockStoreGetState.mockReturnValue({
532+
meteor: { connected: false },
533+
login: { user: null, isAuthenticated: false },
534+
settings: {}
535+
});
536+
537+
// Wire up database.active so the WatermelonDB section of the handler runs.
538+
(database.active as any).get = jest.fn(() => ({
539+
query: () => ({ fetch: () => mockFetch() })
540+
}));
541+
(database.active as any).write = (fn: () => Promise<void>) => mockDbWrite(fn);
542+
543+
await connect({ server: 'https://example.com' });
544+
});
545+
546+
const getUpdateAvatarHandler = () => getHandlersByEvent('stream-notify-logged')[0];
547+
548+
const fireUpdateAvatar = async (args: { username: string; etag: string }) => {
549+
const handler = getUpdateAvatarHandler();
550+
handler({ fields: { eventName: 'updateAvatar', args: [args] } });
551+
await flushMicrotasks();
552+
};
553+
554+
it('dispatches setUser with the new etag when the avatar belongs to the logged user', async () => {
555+
mockStoreGetState.mockReturnValue({
556+
meteor: { connected: true },
557+
login: { user: { id: 'u1', username: 'rocket.cat' }, isAuthenticated: true },
558+
settings: {}
559+
});
560+
561+
await fireUpdateAvatar({ username: 'rocket.cat', etag: 'newEtag' });
562+
563+
expect(mockStoreDispatch).toHaveBeenCalledWith(setUser({ avatarETag: 'newEtag' }));
564+
});
565+
566+
it('does not dispatch setUser when the avatar belongs to another user', async () => {
567+
mockStoreGetState.mockReturnValue({
568+
meteor: { connected: true },
569+
login: { user: { id: 'u1', username: 'rocket.cat' }, isAuthenticated: true },
570+
settings: {}
571+
});
572+
573+
await fireUpdateAvatar({ username: 'someone.else', etag: 'newEtag' });
574+
575+
expect(mockStoreDispatch).not.toHaveBeenCalledWith(setUser({ avatarETag: 'newEtag' }));
576+
});
577+
578+
it('does not dispatch setUser when there is no logged user', async () => {
579+
mockStoreGetState.mockReturnValue({
580+
meteor: { connected: true },
581+
login: { user: null, isAuthenticated: false },
582+
settings: {}
583+
});
584+
585+
await fireUpdateAvatar({ username: 'rocket.cat', etag: 'newEtag' });
586+
587+
expect(mockStoreDispatch).not.toHaveBeenCalledWith(setUser({ avatarETag: 'newEtag' }));
588+
});
589+
590+
it('still updates the users-DB record with the new etag for the logged user', async () => {
591+
mockStoreGetState.mockReturnValue({
592+
meteor: { connected: true },
593+
login: { user: { id: 'u1', username: 'rocket.cat' }, isAuthenticated: true },
594+
settings: {}
595+
});
596+
597+
await fireUpdateAvatar({ username: 'rocket.cat', etag: 'newEtag' });
598+
599+
const updated = { avatarETag: '' };
600+
await mockUserUpdate.mock.calls[0][0](updated);
601+
expect(updated.avatarETag).toBe('newEtag');
602+
});
603+
});
604+
515605
// Note: Apple authentication when isIOS is true is tested in connect.ios.test.ts

app/lib/services/connect.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,15 @@ function connect({ server, logoutOnError = false }: { server: string; logoutOnEr
247247
}
248248
} else if (/updateAvatar/.test(eventName)) {
249249
const { username, etag } = ddpMessage.fields.args[0];
250+
251+
// If it's the logged user, push the new etag through setUser so the
252+
// servers-DB logged-user record (observed by useAvatarETag) updates,
253+
// refreshing the avatar in ProfileView, SidebarView, etc.
254+
const { user: loggedUser } = store.getState().login;
255+
if (loggedUser?.username === username) {
256+
store.dispatch(setUser({ avatarETag: etag }));
257+
}
258+
250259
const db = database.active;
251260
const userCollection = db.get('users');
252261
try {

0 commit comments

Comments
 (0)