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
Showing only changes of commit fbf8e44276 - Show all commits
+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 }) => { withTestScheduler(({ behavior, schedule, expectObservable }) => {
// Local participant enables their video, then disables it // Local participant enables their video, then disables it
const videoInputMarbles = " ny--n"; const videoInputMarbles = " ny--n";
// While tile is shown, tap the screen twice // Tap the screen twice
const tapScreenInputMarbles = "--aa-"; const tapScreenInputMarbles = "--aa-";
// Layout should show local tile, make it small, enlarge it again, then hide it // The local tile is shown throughout; taps make it small, then large again
const expectedLayoutMarbles = "abcba"; const expectedLayoutMarbles = "b-cb";
withCallViewModel( withCallViewModel(
{ {
@@ -477,12 +479,6 @@ describe.each(modes)("CallViewModel (%s mode)", (mode) => {
expectObservable(summarizeLayout$(vm.layout$)).toBe( expectObservable(summarizeLayout$(vm.layout$)).toBe(
expectedLayoutMarbles, expectedLayoutMarbles,
{ {
a: {
type: "one-on-one-mobile",
spotlight: [`${aliceId}:0`],
pip: undefined,
pipSize: "lg",
},
b: { b: {
type: "one-on-one-mobile", type: "one-on-one-mobile",
spotlight: [`${aliceId}:0`], spotlight: [`${aliceId}:0`],
@@ -518,7 +514,7 @@ describe.each(modes)("CallViewModel (%s mode)", (mode) => {
a: { a: {
type: "one-on-one-mobile", type: "one-on-one-mobile",
spotlight: [`${aliceId}:0`], spotlight: [`${aliceId}:0`],
pip: undefined, pip: `${localId}:0`,
pipSize: "lg", pipSize: "lg",
}, },
}); });
@@ -566,7 +562,7 @@ describe.each(modes)("CallViewModel (%s mode)", (mode) => {
a: { a: {
type: "one-on-one-mobile", type: "one-on-one-mobile",
spotlight: [`${aliceId}:0`], spotlight: [`${aliceId}:0`],
pip: undefined, pip: `${localId}:0`,
pipSize: "sm", pipSize: "sm",
}, },
b: { b: {
@@ -765,7 +761,7 @@ describe.each(modes)("CallViewModel (%s mode)", (mode) => {
// This is the expected one-on-one layout for a narrow window // This is the expected one-on-one layout for a narrow window
type: "one-on-one-mobile", type: "one-on-one-mobile",
spotlight: [`${aliceId}:0`], spotlight: [`${aliceId}:0`],
pip: undefined, pip: `${localId}:0`,
pipSize: "lg", pipSize: "lg",
}, },
b: { 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> = const oneOnOneMobileLayoutMedia$: Observable<OneOnOneMobileLayoutMedia | null> =
oneOnOneLayoutMedia$.pipe( oneOnOneLayoutMedia$.pipe(
switchMap((media) => { map((media) =>
if (media === null) return of(null); media === null
return media.local.videoEnabled$.pipe( ? null
map((videoEnabled) => ({ : {
type: "one-on-one-mobile" as const, type: "one-on-one-mobile" as const,
edgeToEdge: true as const, edgeToEdge: true as const,
spotlight: media.remote, spotlight: media.remote,
pip: videoEnabled ? media.local : undefined, pip: media.local,
})), },
); ),
}),
); );
const pipLayoutMedia$: Observable<LayoutMedia> = spotlight$.pipe( const pipLayoutMedia$: Observable<LayoutMedia> = spotlight$.pipe(