Finish the rename in the app's own UI and deep link
The lobby's eyebrow label still read "OFICINA MEET" on the phone. It survived every sweep because it is all caps: the Meeting-safe pattern \bMeet(?!ing) is case-sensitive, so it never matched MEET. That is the same class of miss as \bMeet\b not matching MeetKit — a pattern that was right about word boundaries and wrong about case. Looking for others turned up the deep link, which is the part that mattered. The scheme was still oficina-meet across the server that emits it (handlers_join.go), the app that claims it (CallsModel), both bundle registrations (project.yml, bundle-mac.py) and the tests. It came back in through the merge: main's access-settings branch added the CFBundleURLTypes block after the rename sweep had already run, and my post-merge check grepped for oficina/meet, MEET_, \bMeet\b and meet- — none of which match "oficina-meet". It was not broken, because server and app agreed with each other. It is renamed here rather than left alone because server, app and both bundles have to move together, and this is the one moment when all three are being rebuilt anyway. Server and phone are deployed together; a half-applied rename would break "Join in the app" with the link silently doing nothing. Also moves the WebRTC streamIds from "meet" to "calls" (both peers are this app, so nothing external agrees on it), the Swift test fixture hostnames, and a stale distribute.py reference. "Meet face to face." stays. It is the English verb and still reads correctly under a product called Calls. GOTOOLCHAIN=go1.26.5 make ci passes; swift build and swift test pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
11 files changed,
+18
−18
README.md+1 −1internal/calls/handlers_join.go+1 −1internal/callstest/meeting_settings_test.go+1 −1native/apple/Sources/CallsKit/CallsLobby.swift+1 −1native/apple/Sources/CallsKit/CallsModel.swift+1 −1native/apple/Sources/CallsKit/Media.swift+2 −2native/apple/Tests/CallsKitTests/MeetingLinkTests.swift+5 −5native/ios/project.yml+2 −2native/scripts/bundle-mac.py+2 −2native/scripts/generate-iphone-icon.swift+1 −1test/browser/call.mjs+1 −1
CI passed — run details
diff --git a/README.md b/README.md| index 87bd1bb..5bd3d35 100644 |
| --- a/README.md |
| +++ b/README.md |
| @@ -190,7 +190,7 @@ at entry; admitted participants keep their participation. Turning the waiting ro |
| admits queued guests up to the call capacity. |
| Invitation and browser holding pages offer **Join in the app**, using the |
| -`oficina-meet://join?url=…` URL scheme registered by the Mac and iOS bundles. |
| +`oficina-calls://join?url=…` URL scheme registered by the Mac and iOS bundles. |
| The app opens the meeting link in its lobby for review, then uses the existing |
| browser authorization flow (including anonymous guest sessions). Browser joining |
| remains available when the app is not installed. |
diff --git a/internal/calls/handlers_join.go b/internal/calls/handlers_join.go| index d6aac7b..22365e2 100644 |
| --- a/internal/calls/handlers_join.go |
| +++ b/internal/calls/handlers_join.go |
| @@ -94,5 +94,5 @@ func (a *app) holding(w http.ResponseWriter, r *http.Request) { |
| // Only app-constructed meeting URLs are promoted to the custom URL scheme. |
| func nativeJoinURL(meeting string) template.URL { |
| - return template.URL("oficina-meet://join?url=" + url.QueryEscape(meeting)) |
| + return template.URL("oficina-calls://join?url=" + url.QueryEscape(meeting)) |
| } |
diff --git a/internal/callstest/meeting_settings_test.go b/internal/callstest/meeting_settings_test.go| index 66fe97f..ce44cdc 100644 |
| --- a/internal/callstest/meeting_settings_test.go |
| +++ b/internal/callstest/meeting_settings_test.go |
| @@ -53,7 +53,7 @@ func TestAnonymousGuestJoinsWithoutMemberPowers(t *testing.T) { |
| x, sqid, token := memberWithMeeting(t) |
| g := x.browser(t) |
| r := g.get("/j/" + token) |
| - if r.Code != 200 || !strings.Contains(r.Body.String(), "oficina-meet://join?") { |
| + if r.Code != 200 || !strings.Contains(r.Body.String(), "oficina-calls://join?") { |
| t.Fatal("missing guest/app choice", r.Code) |
| } |
| if r := g.post("/j/"+token+"/guest", url.Values{"name": {" "}}); r.Code != 422 { |
diff --git a/native/apple/Sources/CallsKit/CallsLobby.swift b/native/apple/Sources/CallsKit/CallsLobby.swift| index 182a696..f0e80c9 100644 |
| --- a/native/apple/Sources/CallsKit/CallsLobby.swift |
| +++ b/native/apple/Sources/CallsKit/CallsLobby.swift |
| @@ -15,7 +15,7 @@ import SwiftUI |
| .font(.largeTitle).foregroundStyle(.tint) |
| .padding(18).background(.tint.opacity(0.1), in: RoundedRectangle(cornerRadius: 22)) |
| .accessibilityHidden(true) |
| - Text("OFICINA MEET").font(.caption.bold()).tracking(2).foregroundStyle(.secondary) |
| + Text("OFICINA CALLS").font(.caption.bold()).tracking(2).foregroundStyle(.secondary) |
| Text(heading).font(.largeTitle.bold()).fixedSize(horizontal: false, vertical: true) |
| Text(subheading).foregroundStyle(.secondary).fixedSize(horizontal: false, vertical: true) |
| } |
diff --git a/native/apple/Sources/CallsKit/CallsModel.swift b/native/apple/Sources/CallsKit/CallsModel.swift| index e5a7a62..e6c088f 100644 |
| --- a/native/apple/Sources/CallsKit/CallsModel.swift |
| +++ b/native/apple/Sources/CallsKit/CallsModel.swift |
| @@ -30,7 +30,7 @@ import Foundation |
| error = "Leave the current meeting before opening another." |
| return |
| } |
| - guard url.scheme == "oficina-meet", url.host == "join", |
| + guard url.scheme == "oficina-calls", url.host == "join", |
| let components = URLComponents(url: url, resolvingAgainstBaseURL: false), |
| let target = components.queryItems?.first(where: { $0.name == "url" })?.value, |
| let meeting = URL(string: target), meeting.scheme == "https", |
diff --git a/native/apple/Sources/CallsKit/Media.swift b/native/apple/Sources/CallsKit/Media.swift| index 1621062..12224f2 100644 |
| --- a/native/apple/Sources/CallsKit/Media.swift |
| +++ b/native/apple/Sources/CallsKit/Media.swift |
| @@ -152,8 +152,8 @@ import WebRTC |
| with: configuration(ice), |
| constraints: RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil), |
| delegate: self) |
| - if let audio = engine.localAudio { _ = pc?.add(audio, streamIds: ["meet"]) } |
| - if let video = engine.localVideo { _ = pc?.add(video, streamIds: ["meet"]) } |
| + if let audio = engine.localAudio { _ = pc?.add(audio, streamIds: ["calls"]) } |
| + if let video = engine.localVideo { _ = pc?.add(video, streamIds: ["calls"]) } |
| } |
| func close() { |
| closed = true |
diff --git a/native/apple/Tests/CallsKitTests/MeetingLinkTests.swift b/native/apple/Tests/CallsKitTests/MeetingLinkTests.swift| index d3d2e07..b4dc91b 100644 |
| --- a/native/apple/Tests/CallsKitTests/MeetingLinkTests.swift |
| +++ b/native/apple/Tests/CallsKitTests/MeetingLinkTests.swift |
| @@ -4,16 +4,16 @@ import XCTest |
| final class MeetingLinkTests: XCTestCase { |
| @MainActor func testInvitationOpensInLobbyWithoutJoiningOrStartingAuthentication() { |
| let model = CallsModel() |
| - model.openMeetingURL(URL(string: "oficina-meet://join?url=https%3A%2F%2Fmeet.example.com%2Fj%2Finvitation")!) |
| - XCTAssertEqual(model.address, "https://meet.example.com/j/invitation") |
| + model.openMeetingURL(URL(string: "oficina-calls://join?url=https%3A%2F%2Fcalls.example.com%2Fj%2Finvitation")!) |
| + XCTAssertEqual(model.address, "https://calls.example.com/j/invitation") |
| XCTAssertFalse(model.joining) |
| XCTAssertFalse(model.authenticating) |
| } |
| @MainActor func testRejectsUntrustedURLShapesAndDoesNotInterruptCall() { |
| let model = CallsModel() |
| - for target in ["file:///j/token", "https://user:secret@meet.example.com/j/token", "https://meet.example.com/settings"] { |
| - var link = URLComponents(string: "oficina-meet://join")! |
| + for target in ["file:///j/token", "https://user:secret@calls.example.com/j/token", "https://calls.example.com/settings"] { |
| + var link = URLComponents(string: "oficina-calls://join")! |
| link.queryItems = [URLQueryItem(name: "url", value: target)] |
| model.openMeetingURL(link.url!) |
| XCTAssertEqual(model.address, "") |
| @@ -21,7 +21,7 @@ final class MeetingLinkTests: XCTestCase { |
| } |
| model.joining = true |
| model.address = "current" |
| - model.openMeetingURL(URL(string: "oficina-meet://join?url=https%3A%2F%2Fmeet.example.com%2Fm%2Froom")!) |
| + model.openMeetingURL(URL(string: "oficina-calls://join?url=https%3A%2F%2Fcalls.example.com%2Fm%2Froom")!) |
| XCTAssertEqual(model.address, "current") |
| XCTAssertTrue(model.joining) |
| } |
diff --git a/native/ios/project.yml b/native/ios/project.yml| index 35e85b9..185319d 100644 |
| --- a/native/ios/project.yml |
| +++ b/native/ios/project.yml |
| @@ -25,8 +25,8 @@ targets: |
| path: ../build/ios-info.plist |
| properties: |
| CFBundleURLTypes: |
| - - CFBundleURLName: net.amadan.oficina.meet.join |
| - CFBundleURLSchemes: [oficina-meet] |
| + - CFBundleURLName: net.amadan.oficina.calls.join |
| + CFBundleURLSchemes: [oficina-calls] |
| dependencies: |
| - package: CallsNative |
| product: CallsKit |
diff --git a/native/scripts/bundle-mac.py b/native/scripts/bundle-mac.py| index f034c71..3df0828 100644 |
| --- a/native/scripts/bundle-mac.py |
| +++ b/native/scripts/bundle-mac.py |
| @@ -27,8 +27,8 @@ for size in (16, 32, 128, 256, 512): |
| subprocess.run(['iconutil', '-c', 'icns', str(iconset), '-o', |
| str(bundle / 'Resources/CallsMac.icns')], check=True) |
| info = { |
| - 'CFBundleURLTypes': [{'CFBundleURLName': 'net.amadan.oficina.meet.join', |
| - 'CFBundleURLSchemes': ['oficina-meet']}], |
| + 'CFBundleURLTypes': [{'CFBundleURLName': 'net.amadan.oficina.calls.join', |
| + 'CFBundleURLSchemes': ['oficina-calls']}], |
| 'CFBundleName': 'Calls', 'CFBundleDisplayName': 'Calls', |
| 'CFBundleIdentifier': 'net.amadan.oficina.calls', |
| 'CFBundleExecutable': 'CallsMac', 'CFBundlePackageType': 'APPL', |
diff --git a/native/scripts/generate-iphone-icon.swift b/native/scripts/generate-iphone-icon.swift| index 62a8bcf..e647b87 100644 |
| --- a/native/scripts/generate-iphone-icon.swift |
| +++ b/native/scripts/generate-iphone-icon.swift |
| @@ -1,7 +1,7 @@ |
| import Foundation |
| // Export the approved Oficina artwork already held by the asset catalog. |
| -// Regenerate it from oficina/estilo with brand/distribute.py meet <checkout>. |
| +// Regenerate it from oficina/estilo with brand/distribute.py calls <checkout>. |
| let script = URL(fileURLWithPath: #filePath).standardizedFileURL |
| let native = script.deletingLastPathComponent().deletingLastPathComponent() |
| let source = native.appendingPathComponent("ios/Assets.xcassets/AppIcon.appiconset/AppIcon.png") |
diff --git a/test/browser/call.mjs b/test/browser/call.mjs| index 08dab25..cf2cffe 100644 |
| --- a/test/browser/call.mjs |
| +++ b/test/browser/call.mjs |
| @@ -1150,7 +1150,7 @@ const run = { |
| const guest = await newContext('anonymous guest'); |
| const page = await guest.newPage(); |
| await page.goto(origin + '/j/' + token); |
| - check((await page.getByRole('link', { name: 'Join in the app' }).getAttribute('href')).startsWith('oficina-meet://join?'), 'invitation offers the native app'); |
| + check((await page.getByRole('link', { name: 'Join in the app' }).getAttribute('href')).startsWith('oficina-calls://join?'), 'invitation offers the native app'); |
| await page.getByLabel('Your name').fill('Visitor'); |
| await Promise.all([ |
| page.waitForURL('**/m/' + sqid + '/join'), |