diff --git a/e2e/helpers/seed-test-endpoint.ts b/e2e/helpers/seed-test-endpoint.ts index eff3d97..9713d6a 100644 --- a/e2e/helpers/seed-test-endpoint.ts +++ b/e2e/helpers/seed-test-endpoint.ts @@ -58,6 +58,10 @@ function buildSeededEndpointStorageState( function applySeededEndpointStorageState(storageState: SeededEndpointStorageState): void { try { const storage = window.localStorage; + const currentUserId = storage.getItem('metoyou_currentUserId')?.trim() || null; + const generalSettings = JSON.stringify({ + reopenLastViewedChat: false + }); storage.setItem(storageState.key, JSON.stringify(storageState.endpoints)); storage.setItem(storageState.removedKey, JSON.stringify([ @@ -65,11 +69,56 @@ function applySeededEndpointStorageState(storageState: SeededEndpointStorageStat 'toju-primary', 'toju-sweden' ])); + storage.setItem('metoyou_general_settings', generalSettings); + + if (currentUserId) { + storage.setItem(`metoyou_general_settings__${encodeURIComponent(currentUserId)}`, generalSettings); + } + + const keysToRemove: string[] = []; + + for (let index = 0; index < storage.length; index += 1) { + const key = storage.key(index); + + if (key === 'metoyou_lastViewedChat' || key?.startsWith('metoyou_lastViewedChat__')) { + keysToRemove.push(key); + } + } + + for (const key of keysToRemove) { + storage.removeItem(key); + } } catch { // about:blank and some Playwright UI pages deny localStorage access. } } +export async function disableLastViewedChatResume(page: Page): Promise { + await page.evaluate(() => { + const currentUserId = localStorage.getItem('metoyou_currentUserId')?.trim() || null; + const generalSettings = JSON.stringify({ reopenLastViewedChat: false }); + const keysToRemove: string[] = []; + + localStorage.setItem('metoyou_general_settings', generalSettings); + + if (currentUserId) { + localStorage.setItem(`metoyou_general_settings__${encodeURIComponent(currentUserId)}`, generalSettings); + } + + for (let index = 0; index < localStorage.length; index += 1) { + const key = localStorage.key(index); + + if (key === 'metoyou_lastViewedChat' || key?.startsWith('metoyou_lastViewedChat__')) { + keysToRemove.push(key); + } + } + + for (const key of keysToRemove) { + localStorage.removeItem(key); + } + }); +} + export async function installTestServerEndpoint( context: BrowserContext, port: number = Number(process.env.TEST_SERVER_PORT) || 3099 diff --git a/e2e/tests/chat/dm-flow.spec.ts b/e2e/tests/chat/dm-flow.spec.ts index 84c26bd..6105c8d 100644 --- a/e2e/tests/chat/dm-flow.spec.ts +++ b/e2e/tests/chat/dm-flow.spec.ts @@ -7,6 +7,7 @@ import { import { RegisterPage } from '../../pages/register.page'; import { ServerSearchPage } from '../../pages/server-search.page'; import { ChatMessagesPage } from '../../pages/chat-messages.page'; +import { disableLastViewedChatResume } from '../../helpers/seed-test-endpoint'; test.describe('Direct message flow', () => { test.describe.configure({ timeout: 180_000 }); @@ -37,6 +38,7 @@ test.describe('Direct message flow', () => { test('shows friend and message actions on the search people list', async ({ createClient }) => { const scenario = await createDmScenario(createClient); + await disableLastViewedChatResume(scenario.alice.page); await scenario.alice.page.goto('/search', { waitUntil: 'domcontentloaded' }); await expect(scenario.alice.page).toHaveURL(/\/search/, { timeout: 20_000 }); await expect(scenario.alice.page.locator('app-server-search')).toBeVisible({ timeout: 20_000 }); diff --git a/e2e/tests/chat/profile-avatar-sync.spec.ts b/e2e/tests/chat/profile-avatar-sync.spec.ts index 0f32d6c..c4e52c1 100644 --- a/e2e/tests/chat/profile-avatar-sync.spec.ts +++ b/e2e/tests/chat/profile-avatar-sync.spec.ts @@ -69,6 +69,7 @@ const NETSCAPE_LOOP_EXTENSION = Buffer.from([ ]); const CLIENT_LAUNCH_ARGS = ['--use-fake-device-for-media-stream', '--use-fake-ui-for-media-stream']; const VOICE_CHANNEL = 'General'; +const AVATAR_SYNC_TIMEOUT_MS = 45_000; test.describe('Profile avatar sync', () => { test.describe.configure({ timeout: 240_000 }); @@ -598,7 +599,7 @@ async function expectSidebarAvatar(page: Page, displayName: string, expectedData return image.getAttribute('src'); }, { - timeout: 20_000, + timeout: AVATAR_SYNC_TIMEOUT_MS, message: `${displayName} avatar src should update` }).toBe(expectedDataUrl); @@ -615,7 +616,7 @@ async function expectSidebarAvatar(page: Page, displayName: string, expectedData return img.complete && img.naturalWidth > 0 && img.naturalHeight > 0; }); }, { - timeout: 20_000, + timeout: AVATAR_SYNC_TIMEOUT_MS, message: `${displayName} avatar image should load` }).toBe(true); } @@ -635,7 +636,7 @@ async function expectChatMessageAvatar(page: Page, messageText: string, expected return image.getAttribute('src'); }, { - timeout: 20_000, + timeout: AVATAR_SYNC_TIMEOUT_MS, message: `Chat message avatar for "${messageText}" should update` }).toBe(expectedDataUrl); } @@ -662,7 +663,7 @@ async function expectVoiceControlsAvatar(page: Page, expectedDataUrl: string): P return image.getAttribute('src'); }, { - timeout: 20_000, + timeout: AVATAR_SYNC_TIMEOUT_MS, message: 'Voice controls avatar should update' }).toBe(expectedDataUrl); } diff --git a/toju-app/src/app/infrastructure/realtime/peer-connection-manager/messaging/data-channel.ts b/toju-app/src/app/infrastructure/realtime/peer-connection-manager/messaging/data-channel.ts index 56aece0..04dc76b 100644 --- a/toju-app/src/app/infrastructure/realtime/peer-connection-manager/messaging/data-channel.ts +++ b/toju-app/src/app/infrastructure/realtime/peer-connection-manager/messaging/data-channel.ts @@ -29,7 +29,7 @@ export function setupDataChannel( channel: RTCDataChannel, remotePeerId: string ): void { - const { logger } = context; + const { logger, state } = context; channel.onopen = () => { logger.info('[data-channel] Data channel open', { @@ -40,6 +40,8 @@ export function setupDataChannel( protocol: channel.protocol || null }); + state.peerConnected$.next(remotePeerId); + sendCurrentStatesToChannel(context, channel, remotePeerId); try {