fix: solve small pm chat ui issues
unwrap the pill fix the fetching images in pm not auto download
This commit is contained in:
+119
-9
@@ -9,6 +9,7 @@ import { Router } from '@angular/router';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Subject } from 'rxjs';
|
||||
import { NotificationAudioService, AppSound } from '../../../../core/services/notification-audio.service';
|
||||
import { ViewportService } from '../../../../core/platform';
|
||||
import {
|
||||
VoiceActivityService,
|
||||
VoiceConnectionFacade,
|
||||
@@ -136,6 +137,87 @@ describe('DirectCallService', () => {
|
||||
expect(context.service.incomingCall()).toBeNull();
|
||||
});
|
||||
|
||||
it('does not start ringing after declining while incoming ring setup is still pending', async () => {
|
||||
let releaseCallLog: (() => void) | null = null;
|
||||
|
||||
const context = createServiceContext({ currentUser: bob, allUsers: [alice, bob] });
|
||||
|
||||
context.directMessages.recordCallStarted.mockImplementationOnce(async () => new Promise<void>((resolve) => {
|
||||
releaseCallLog = resolve;
|
||||
}));
|
||||
|
||||
context.directCallEvents.next(createCallEvent('ring', alice, ['alice', 'bob']));
|
||||
|
||||
await vi.waitFor(() => expect(context.service.incomingCall()?.callId).toBe('dm-alice-bob'));
|
||||
await vi.waitFor(() => expect(context.directMessages.recordCallStarted).toHaveBeenCalled());
|
||||
context.service.declineIncomingCall('dm-alice-bob');
|
||||
releaseCallLog?.();
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
|
||||
expect(context.service.incomingCall()).toBeNull();
|
||||
expect(context.audio.playLoop).not.toHaveBeenCalled();
|
||||
expect(context.audio.stop).toHaveBeenCalledWith(AppSound.Call);
|
||||
});
|
||||
|
||||
it('logs a system call-started entry when starting a direct PM call', async () => {
|
||||
const context = createServiceContext({ currentUser: alice, allUsers: [alice, bob] });
|
||||
|
||||
await context.service.startCall(bob);
|
||||
|
||||
expect(context.directMessages.recordCallStarted).toHaveBeenCalledWith(
|
||||
'dm-alice-bob',
|
||||
expect.objectContaining({ userId: 'alice' }),
|
||||
expect.arrayContaining([expect.objectContaining({ userId: 'alice' }), expect.objectContaining({ userId: 'bob' })]),
|
||||
expect.any(Number)
|
||||
);
|
||||
});
|
||||
|
||||
it('logs a system call-started entry when receiving a PM ring', async () => {
|
||||
const context = createServiceContext({ currentUser: bob, allUsers: [alice, bob] });
|
||||
|
||||
context.directCallEvents.next(createCallEvent('ring', alice, ['alice', 'bob']));
|
||||
|
||||
await vi.waitFor(() => expect(context.service.incomingCall()?.callId).toBe('dm-alice-bob'));
|
||||
await vi.waitFor(() => expect(context.directMessages.recordCallStarted).toHaveBeenCalledWith(
|
||||
'dm-alice-bob',
|
||||
expect.objectContaining({ userId: 'alice' }),
|
||||
expect.arrayContaining([expect.objectContaining({ userId: 'alice' }), expect.objectContaining({ userId: 'bob' })]),
|
||||
10
|
||||
));
|
||||
});
|
||||
|
||||
it('does not restart the call sound when a stale ring arrives after declining', async () => {
|
||||
const context = createServiceContext({ currentUser: bob, allUsers: [alice, bob] });
|
||||
|
||||
context.directCallEvents.next(createCallEvent('ring', alice, ['alice', 'bob']));
|
||||
|
||||
await vi.waitFor(() => expect(context.service.incomingCall()?.callId).toBe('dm-alice-bob'));
|
||||
context.service.declineIncomingCall('dm-alice-bob');
|
||||
context.audio.playLoop.mockClear();
|
||||
|
||||
context.directCallEvents.next(createCallEvent('ring', alice, ['alice', 'bob']));
|
||||
|
||||
await vi.waitFor(() => expect(context.service.sessionById('dm-alice-bob')?.status).toBe('ended'));
|
||||
expect(context.service.incomingCall()).toBeNull();
|
||||
expect(context.audio.playLoop).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('shows a pending incoming call once the current user hydrates', async () => {
|
||||
const context = createServiceContext({ currentUser: null, allUsers: [alice, bob] });
|
||||
|
||||
context.directCallEvents.next(createCallEvent('ring', alice, ['alice', 'bob']));
|
||||
|
||||
await Promise.resolve();
|
||||
expect(context.service.sessionById('dm-alice-bob')).toBeNull();
|
||||
|
||||
context.currentUser.set(bob);
|
||||
context.effectScheduler.flush();
|
||||
|
||||
await vi.waitFor(() => expect(context.service.incomingCall()?.callId).toBe('dm-alice-bob'));
|
||||
await vi.waitFor(() => expect(context.audio.playLoop).toHaveBeenCalledWith(AppSound.Call));
|
||||
});
|
||||
|
||||
it('rejoins an existing direct call instead of ringing a duplicate after leaving locally', async () => {
|
||||
const context = createServiceContext({ currentUser: alice, allUsers: [alice, bob] });
|
||||
const session = createSession('connected', true);
|
||||
@@ -298,7 +380,7 @@ describe('DirectCallService', () => {
|
||||
|
||||
interface ServiceContextOptions {
|
||||
allUsers: User[];
|
||||
currentUser: User;
|
||||
currentUser: User | null;
|
||||
}
|
||||
|
||||
interface ServiceContext {
|
||||
@@ -306,6 +388,7 @@ interface ServiceContext {
|
||||
playLoop: ReturnType<typeof vi.fn>;
|
||||
stop: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
currentUser: ReturnType<typeof signal<User | null>>;
|
||||
delivery: {
|
||||
sendCallEvent: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
@@ -314,6 +397,10 @@ interface ServiceContext {
|
||||
createConversation: ReturnType<typeof vi.fn>;
|
||||
createGroupConversation: ReturnType<typeof vi.fn>;
|
||||
openConversation: ReturnType<typeof vi.fn>;
|
||||
recordCallStarted: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
effectScheduler: {
|
||||
flush: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
router: {
|
||||
navigate: ReturnType<typeof vi.fn>;
|
||||
@@ -351,12 +438,13 @@ function createServiceContext(options: ServiceContextOptions): ServiceContext {
|
||||
})
|
||||
};
|
||||
const directMessages = {
|
||||
createConversation: vi.fn(async (user: User) => createDirectConversation(options.currentUser, user)),
|
||||
createConversation: vi.fn(async (user: User) => createDirectConversation(currentUser() ?? bob, user)),
|
||||
createGroupConversation: vi.fn(async (participants: DirectMessageParticipant[], title?: string, conversationId = 'dm-group-test') => ({
|
||||
...createGroupConversation(conversationId, participants.map(participantToUser)),
|
||||
title
|
||||
})),
|
||||
openConversation: vi.fn(async () => undefined)
|
||||
openConversation: vi.fn(async () => undefined),
|
||||
recordCallStarted: vi.fn(async () => undefined)
|
||||
};
|
||||
const delivery = {
|
||||
directCallEvents$: directCallEvents.asObservable(),
|
||||
@@ -366,6 +454,25 @@ function createServiceContext(options: ServiceContextOptions): ServiceContext {
|
||||
playLoop: vi.fn(),
|
||||
stop: vi.fn()
|
||||
};
|
||||
const scheduledEffects = new Set<{ dirty: boolean; run: () => void }>();
|
||||
const effectScheduler = {
|
||||
add: vi.fn((scheduledEffect: { dirty: boolean; run: () => void }) => {
|
||||
scheduledEffects.add(scheduledEffect);
|
||||
}),
|
||||
flush: vi.fn(() => {
|
||||
for (const scheduledEffect of scheduledEffects) {
|
||||
if (scheduledEffect.dirty) {
|
||||
scheduledEffect.run();
|
||||
}
|
||||
}
|
||||
}),
|
||||
remove: vi.fn((scheduledEffect: { dirty: boolean; run: () => void }) => {
|
||||
scheduledEffects.delete(scheduledEffect);
|
||||
}),
|
||||
schedule: vi.fn((scheduledEffect: { dirty: boolean; run: () => void }) => {
|
||||
scheduledEffects.add(scheduledEffect);
|
||||
})
|
||||
};
|
||||
const voice = {
|
||||
broadcastMessage: vi.fn(),
|
||||
disableVoice: vi.fn(),
|
||||
@@ -391,12 +498,7 @@ function createServiceContext(options: ServiceContextOptions): ServiceContext {
|
||||
},
|
||||
{
|
||||
provide: EffectScheduler,
|
||||
useValue: {
|
||||
add: vi.fn(),
|
||||
flush: vi.fn(),
|
||||
remove: vi.fn(),
|
||||
schedule: vi.fn()
|
||||
}
|
||||
useValue: effectScheduler
|
||||
},
|
||||
{
|
||||
provide: DirectMessageService,
|
||||
@@ -439,15 +541,23 @@ function createServiceContext(options: ServiceContextOptions): ServiceContext {
|
||||
playPendingStreams: vi.fn(),
|
||||
teardownAll: vi.fn()
|
||||
}
|
||||
},
|
||||
{
|
||||
provide: ViewportService,
|
||||
useValue: {
|
||||
isMobile: vi.fn(() => false)
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return {
|
||||
audio,
|
||||
currentUser,
|
||||
delivery,
|
||||
directCallEvents,
|
||||
directMessages,
|
||||
effectScheduler,
|
||||
router,
|
||||
service: runInInjectionContext(injector, () => new DirectCallService()),
|
||||
voice,
|
||||
|
||||
Reference in New Issue
Block a user