feat(lotus): keep your own tile on mobile with the camera off (cinny #265) #58

Merged
jared merged 1 commits from feat/mobile-self-tile into lotus 2026-10-19 14:58:36 -04:00
2 changed files with 23 additions and 24 deletions
+9 -13
View File
@@ -451,14 +451,16 @@ describe.each(modes)("CallViewModel (%s mode)", (mode) => {
});
});
test("one-on-one mobile layout shows local tile when video is enabled", () => {
// [lotus / cinny #265] The local tile stays on mobile with the camera off
// (upstream hid it), so you can see yourself in a voice call.
test("one-on-one mobile layout shows the local tile whether or not video is enabled", () => {
withTestScheduler(({ behavior, schedule, expectObservable }) => {
// Local participant enables their video, then disables it
const videoInputMarbles = " ny--n";
// While tile is shown, tap the screen twice
// Tap the screen twice
const tapScreenInputMarbles = "--aa-";
// Layout should show local tile, make it small, enlarge it again, then hide it
const expectedLayoutMarbles = "abcba";
// The local tile is shown throughout; taps make it small, then large again
const expectedLayoutMarbles = "b-cb";
withCallViewModel(
{
@@ -477,12 +479,6 @@ describe.each(modes)("CallViewModel (%s mode)", (mode) => {
expectObservable(summarizeLayout$(vm.layout$)).toBe(
expectedLayoutMarbles,
{
a: {
type: "one-on-one-mobile",
spotlight: [`${aliceId}:0`],
pip: undefined,
pipSize: "lg",
},
b: {
type: "one-on-one-mobile",
spotlight: [`${aliceId}:0`],
@@ -518,7 +514,7 @@ describe.each(modes)("CallViewModel (%s mode)", (mode) => {
a: {
type: "one-on-one-mobile",
spotlight: [`${aliceId}:0`],
pip: undefined,
pip: `${localId}:0`,
pipSize: "lg",
},
});
@@ -566,7 +562,7 @@ describe.each(modes)("CallViewModel (%s mode)", (mode) => {
a: {
type: "one-on-one-mobile",
spotlight: [`${aliceId}:0`],
pip: undefined,
pip: `${localId}:0`,
pipSize: "sm",
},
b: {
@@ -765,7 +761,7 @@ describe.each(modes)("CallViewModel (%s mode)", (mode) => {
// This is the expected one-on-one layout for a narrow window
type: "one-on-one-mobile",
spotlight: [`${aliceId}:0`],
pip: undefined,
pip: `${localId}:0`,
pipSize: "lg",
},
b: {
+14 -11
View File
@@ -1220,19 +1220,22 @@ export function createCallViewModel$(
}),
);
// [lotus / cinny #265] Upstream drops the local PiP on mobile while your
// camera is off; in Lotus voice rooms that is almost always, so on a phone
// you never saw yourself (or your own speaking ring). Show it as desktop
// does: the tile falls back to your avatar.
const oneOnOneMobileLayoutMedia$: Observable<OneOnOneMobileLayoutMedia | null> =
oneOnOneLayoutMedia$.pipe(
switchMap((media) => {
if (media === null) return of(null);
return media.local.videoEnabled$.pipe(
map((videoEnabled) => ({
type: "one-on-one-mobile" as const,
edgeToEdge: true as const,
spotlight: media.remote,
pip: videoEnabled ? media.local : undefined,
})),
);
}),
map((media) =>
media === null
? null
: {
type: "one-on-one-mobile" as const,
edgeToEdge: true as const,
spotlight: media.remote,
pip: media.local,
},
),
);
const pipLayoutMedia$: Observable<LayoutMedia> = spotlight$.pipe(