diff --git a/e2e/page-objects/calls-page.po.ts b/e2e/page-objects/calls-page.po.ts index d36df5c9..ef871bf9 100644 --- a/e2e/page-objects/calls-page.po.ts +++ b/e2e/page-objects/calls-page.po.ts @@ -13,7 +13,7 @@ export class CallsPagePO { } async gotoWithParams(productionId: string, lineId: string) { - await this.page.goto(`/lines?lines=${productionId}:${lineId}`); + await this.page.goto(`/calls?lines=${productionId}:${lineId}`); } async gotoWithSettings( @@ -30,6 +30,6 @@ export class CallsPagePO { }, { username } ); - await this.page.goto(`/lines?lines=${productionId}:${lineId}`); + await this.page.goto(`/calls?lines=${productionId}:${lineId}`); } } diff --git a/e2e/tests/calls-page.spec.ts b/e2e/tests/calls-page.spec.ts index 63ef1fa4..e78373d3 100644 --- a/e2e/tests/calls-page.spec.ts +++ b/e2e/tests/calls-page.spec.ts @@ -7,7 +7,7 @@ test.describe("Calls Page", () => { callsPage, }) => { await callsPage.gotoWithParams("1", "10"); - await expect(callsPage.page).toHaveURL(/\/lines\?lines=1:10/); + await expect(callsPage.page).toHaveURL(/\/calls\?lines=1:10/); }); test("shows join form when no username is set", async ({ callsPage }) => { @@ -17,11 +17,11 @@ test.describe("Calls Page", () => { ).toBeVisible(); }); - test("shows Lines header after joining with username", async ({ + test("shows Calls header after joining with username", async ({ callsPage, }) => { await callsPage.gotoWithSettings("1", "10"); - await expect(callsPage.page.getByText("Lines").first()).toBeVisible(); + await expect(callsPage.page.getByText("Calls").first()).toBeVisible(); }); test("shows Save as Configuration button when not on mobile", async ({ @@ -57,7 +57,7 @@ test.describe("Calls Page", () => { test("invalid line in URL is removed silently", async ({ callsPage }) => { // Line 99 does not exist; only line 10 should remain - await callsPage.page.goto("/lines?lines=1:10,1:99"); + await callsPage.page.goto("/calls?lines=1:10,1:99"); await expect(callsPage.page).toHaveURL(/lines=1:10/); await expect(callsPage.page).not.toHaveURL(/1:99/); }); diff --git a/e2e/tests/landing-page.spec.ts b/e2e/tests/landing-page.spec.ts index 72039191..39e8d6c4 100644 --- a/e2e/tests/landing-page.spec.ts +++ b/e2e/tests/landing-page.spec.ts @@ -98,6 +98,6 @@ test.describe("Landing Page", () => { .getByText("Quick Join Preset") .locator(".."); await presetCard.getByRole("button", { name: /^join$/i }).click(); - await expect(landingPage.page).toHaveURL(/\/lines\?lines=1:10/); + await expect(landingPage.page).toHaveURL(/\/calls\?lines=1:10/); }); }); diff --git a/e2e/tests/share-and-modals.spec.ts b/e2e/tests/share-and-modals.spec.ts index f48d3192..d5ed9dd0 100644 --- a/e2e/tests/share-and-modals.spec.ts +++ b/e2e/tests/share-and-modals.spec.ts @@ -8,7 +8,7 @@ test.describe("Share Link Modal", () => { await landingPage.page.getByTitle("Get share link").first().click(); // Share modal should appear - await expect(landingPage.page.getByText("Share Lines")).toBeVisible(); + await expect(landingPage.page.getByText("Share Calls")).toBeVisible(); }); test("share modal shows note about single use links", async ({ diff --git a/src/App.tsx b/src/App.tsx index cb45cbff..264e07f1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,12 @@ import styled from "@emotion/styled"; import { useEffect, useState } from "react"; -import { BrowserRouter, Routes, Route } from "react-router"; +import { + BrowserRouter, + Routes, + Route, + Navigate, + useLocation, +} from "react-router"; import { ErrorPage } from "./components/router-error.tsx"; import { useDevicePermissions } from "./hooks/use-device-permission.ts"; import { LandingPage } from "./components/landing-page/landing-page.tsx"; @@ -36,6 +42,11 @@ const ButtonWrapper = styled.div` display: inline-block; `; +const LinesToCallsRedirect = () => { + const { search } = useLocation(); + return ; +}; + const NotFound = () => { return ( @@ -166,10 +177,11 @@ const AppContent = ({ errorElement={} /> } errorElement={} /> + } /> } /> diff --git a/src/components/calls-page/calls-page.tsx b/src/components/calls-page/calls-page.tsx index a24d9685..fad32c05 100644 --- a/src/components/calls-page/calls-page.tsx +++ b/src/components/calls-page/calls-page.tsx @@ -450,7 +450,7 @@ export const CallsPage = () => { setShowSettings(!showSettings)} /> )} @@ -477,8 +477,8 @@ export const CallsPage = () => { {confirmExitModalOpen && ( setConfirmExitModalOpen(false)} onConfirm={runExitAllCalls} /> diff --git a/src/components/calls-page/header-actions.tsx b/src/components/calls-page/header-actions.tsx index 7e71edc2..6bf31e9c 100644 --- a/src/components/calls-page/header-actions.tsx +++ b/src/components/calls-page/header-actions.tsx @@ -146,7 +146,7 @@ export const HeaderActions = ({ type="button" onClick={() => setAddCallActive(!addCallActive)} > - Add Line + Add Call )} diff --git a/src/components/calls-page/use-calls-navigation.test.ts b/src/components/calls-page/use-calls-navigation.test.ts index 38720304..590f596f 100644 --- a/src/components/calls-page/use-calls-navigation.test.ts +++ b/src/components/calls-page/use-calls-navigation.test.ts @@ -18,7 +18,7 @@ describe("useCallsNavigation", () => { mockNavigate.mockReset(); mockSearchParams = new URLSearchParams(); Object.defineProperty(window, "location", { - value: { pathname: "/lines", search: "" }, + value: { pathname: "/calls", search: "" }, writable: true, }); }); @@ -29,7 +29,7 @@ describe("useCallsNavigation", () => { // a pending program card that has not yet appeared in the URL). mockSearchParams = new URLSearchParams("lines=1:10"); Object.defineProperty(window, "location", { - value: { pathname: "/lines", search: "?lines=1:10" }, + value: { pathname: "/calls", search: "?lines=1:10" }, writable: true, }); @@ -64,7 +64,7 @@ describe("useCallsNavigation", () => { it("does not change the URL when a program line ref has joined and is no longer pending", async () => { mockSearchParams = new URLSearchParams("lines=1:10,1:20"); Object.defineProperty(window, "location", { - value: { pathname: "/lines", search: "?lines=1:10,1:20" }, + value: { pathname: "/calls", search: "?lines=1:10,1:20" }, writable: true, }); @@ -98,7 +98,7 @@ describe("useCallsNavigation", () => { // Original URL only had line 10 mockSearchParams = new URLSearchParams("lines=1:10"); Object.defineProperty(window, "location", { - value: { pathname: "/lines", search: "?lines=1:10" }, + value: { pathname: "/calls", search: "?lines=1:10" }, writable: true, }); @@ -140,7 +140,7 @@ describe("useCallsNavigation", () => { // window.location already contains the companion param — the source of truth Object.defineProperty(window, "location", { value: { - pathname: "/lines", + pathname: "/calls", search: "?lines=1:10&companion=localhost:9000", }, writable: true, @@ -180,7 +180,7 @@ describe("useCallsNavigation", () => { mockSearchParams = new URLSearchParams("lines=1:10"); // stale — no companion yet Object.defineProperty(window, "location", { value: { - pathname: "/lines", + pathname: "/calls", search: "?lines=1:10&companion=host:9000", }, writable: true, @@ -232,7 +232,7 @@ describe("useCallsNavigation", () => { // Mock window.location so the hook's guard detects no change Object.defineProperty(window, "location", { - value: { pathname: "/lines", search: "?lines=1:10" }, + value: { pathname: "/calls", search: "?lines=1:10" }, writable: true, }); diff --git a/src/components/generate-urls/share-line-link/share-line-link-modal.tsx b/src/components/generate-urls/share-line-link/share-line-link-modal.tsx index 822a516a..40dde9b1 100644 --- a/src/components/generate-urls/share-line-link/share-line-link-modal.tsx +++ b/src/components/generate-urls/share-line-link/share-line-link-modal.tsx @@ -204,12 +204,12 @@ export const ShareLineLinkModal = ({ return ( {isCopyProduction - ? "Share these links to invite others to each line." - : "Anyone with this link can join the line."} + ? "Share these links to invite others to each call." + : "Anyone with this link can join the call."} Each link can only be used once. A fresh link is generated automatically diff --git a/src/components/landing-page/join-production.tsx b/src/components/landing-page/join-production.tsx index ef93a24c..2431b0d4 100644 --- a/src/components/landing-page/join-production.tsx +++ b/src/components/landing-page/join-production.tsx @@ -69,7 +69,7 @@ export const JoinProduction = ({ className={`${isMobile ? "" : "desktop"} ${className}`} > - Join Production + Join Call {closeAddCallView && ( closeAddCallView()}> diff --git a/src/components/production-line/exit-call-button.tsx b/src/components/production-line/exit-call-button.tsx index bae1d581..e57ba39a 100644 --- a/src/components/production-line/exit-call-button.tsx +++ b/src/components/production-line/exit-call-button.tsx @@ -40,7 +40,7 @@ export const ExitCallButton = ({ title="Exit line" onClick={() => resetOnExit()} > - Leave Line + Leave Call ); diff --git a/src/components/production-line/settings-modal.tsx b/src/components/production-line/settings-modal.tsx index a66505b2..6a8a9cc8 100644 --- a/src/components/production-line/settings-modal.tsx +++ b/src/components/production-line/settings-modal.tsx @@ -106,7 +106,7 @@ export const SettingsModal = ({ : false; if (isGlobalStateDuplicate) { - acc[field] = "This key is used in another connected line."; + acc[field] = "This key is used in another connected call."; } else { acc[field] = ""; } @@ -224,7 +224,7 @@ export const SettingsModal = ({ return ( diff --git a/src/utils/call-url.test.ts b/src/utils/call-url.test.ts index 94784c4e..04932d45 100644 --- a/src/utils/call-url.test.ts +++ b/src/utils/call-url.test.ts @@ -7,17 +7,17 @@ import { } from "./call-url"; describe("buildCallsUrl", () => { - it("returns /lines when given an empty list", () => { - expect(buildCallsUrl([])).toBe("/lines"); + it("returns /calls when given an empty list", () => { + expect(buildCallsUrl([])).toBe("/calls"); }); - it("produces the /lines?lines=prodId:lineId format for a single call", () => { + it("produces the /calls?lines=prodId:lineId format for a single call", () => { // Regression: use-navigate-to-production previously navigated to the old // /production-calls/production/:id/line/:id URL. This assertion locks in // the correct target URL that buildCallsUrl must produce so that the landing // page join lands directly on the right route without a flash redirect. expect(buildCallsUrl([{ productionId: "prod-1", lineId: "line-2" }])).toBe( - "/lines?lines=prod-1:line-2" + "/calls?lines=prod-1:line-2" ); }); @@ -27,7 +27,7 @@ describe("buildCallsUrl", () => { { productionId: "p1", lineId: "l1" }, { productionId: "p2", lineId: "l2" }, ]) - ).toBe("/lines?lines=p1:l1,p2:l2"); + ).toBe("/calls?lines=p1:l1,p2:l2"); }); }); @@ -94,7 +94,7 @@ describe("buildCallsUrl — companion URL", () => { [{ productionId: "p1", lineId: "l1" }], "ws://localhost:12345" ); - expect(url).toBe("/lines?lines=p1:l1&companion=localhost:12345"); + expect(url).toBe("/calls?lines=p1:l1&companion=localhost:12345"); }); it("strips wss:// prefix from companion URL", () => { @@ -102,17 +102,17 @@ describe("buildCallsUrl — companion URL", () => { [{ productionId: "p1", lineId: "l1" }], "wss://example.com:9000" ); - expect(url).toBe("/lines?lines=p1:l1&companion=example.com:9000"); + expect(url).toBe("/calls?lines=p1:l1&companion=example.com:9000"); }); it("works with empty calls list and a companion URL", () => { const url = buildCallsUrl([], "ws://localhost:12345"); - expect(url).toBe("/lines?companion=localhost:12345"); + expect(url).toBe("/calls?companion=localhost:12345"); }); it("returns the base URL unchanged when companionUrl is undefined", () => { expect(buildCallsUrl([{ productionId: "p1", lineId: "l1" }])).toBe( - "/lines?lines=p1:l1" + "/calls?lines=p1:l1" ); }); }); diff --git a/src/utils/call-url.ts b/src/utils/call-url.ts index 592302cb..268087ff 100644 --- a/src/utils/call-url.ts +++ b/src/utils/call-url.ts @@ -32,7 +32,7 @@ export function decodeCallsParam(param: string | null): CallRef[] { export function buildCallsUrl(calls: CallRef[], companionUrl?: string): string { const base = - calls.length === 0 ? "/lines" : `/lines?lines=${encodeCallsParam(calls)}`; + calls.length === 0 ? "/calls" : `/calls?lines=${encodeCallsParam(calls)}`; let url = base; if (companionUrl) { const hostPort = companionUrl.replace(/^wss?:\/\//, "");