Add ring notification to UserIntent.StartNewCallDM (#3499)

* Add ring notification to UserIntent.StartNewCallDM

Signed-off-by: Timo K <toger5@hotmail.de>

* Add more tests (refactor to compute + get)

Signed-off-by: Timo K <toger5@hotmail.de>

---------

Signed-off-by: Timo K <toger5@hotmail.de>
This commit is contained in:
Timo
2025-09-18 17:45:49 +02:00
committed by GitHub
parent db5c7cf9c7
commit 6e9dc34008
2 changed files with 108 additions and 41 deletions

View File

@@ -5,12 +5,15 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE in the repository root for full details. Please see LICENSE in the repository root for full details.
*/ */
import { describe, expect, it } from "vitest"; import { describe, expect, it, onTestFinished, vi } from "vitest";
import { logger } from "matrix-js-sdk/lib/logger";
import * as PlatformMod from "../src/Platform";
import { import {
getRoomIdentifierFromUrl, getRoomIdentifierFromUrl,
getUrlParams, computeUrlParams,
HeaderStyle, HeaderStyle,
getUrlParams,
} from "../src/UrlParams"; } from "../src/UrlParams";
const ROOM_NAME = "roomNameHere"; const ROOM_NAME = "roomNameHere";
@@ -103,16 +106,16 @@ describe("UrlParams", () => {
describe("preload", () => { describe("preload", () => {
it("defaults to false", () => { it("defaults to false", () => {
expect(getUrlParams().preload).toBe(false); expect(computeUrlParams().preload).toBe(false);
}); });
it("ignored in SPA mode", () => { it("ignored in SPA mode", () => {
expect(getUrlParams("?preload=true").preload).toBe(false); expect(computeUrlParams("?preload=true").preload).toBe(false);
}); });
it("respected in widget mode", () => { it("respected in widget mode", () => {
expect( expect(
getUrlParams( computeUrlParams(
"?preload=true&widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo", "?preload=true&widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo",
).preload, ).preload,
).toBe(true); ).toBe(true);
@@ -121,19 +124,20 @@ describe("UrlParams", () => {
describe("returnToLobby", () => { describe("returnToLobby", () => {
it("is false in SPA mode", () => { it("is false in SPA mode", () => {
expect(getUrlParams("?returnToLobby=true").returnToLobby).toBe(false); expect(computeUrlParams("?returnToLobby=true").returnToLobby).toBe(false);
}); });
it("defaults to false in widget mode", () => { it("defaults to false in widget mode", () => {
expect( expect(
getUrlParams("?widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo") computeUrlParams(
.returnToLobby, "?widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo",
).returnToLobby,
).toBe(false); ).toBe(false);
}); });
it("respected in widget mode", () => { it("respected in widget mode", () => {
expect( expect(
getUrlParams( computeUrlParams(
"?returnToLobby=true&widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo", "?returnToLobby=true&widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo",
).returnToLobby, ).returnToLobby,
).toBe(true); ).toBe(true);
@@ -142,12 +146,12 @@ describe("UrlParams", () => {
describe("userId", () => { describe("userId", () => {
it("is ignored in SPA mode", () => { it("is ignored in SPA mode", () => {
expect(getUrlParams("?userId=asd").userId).toBe(null); expect(computeUrlParams("?userId=asd").userId).toBe(null);
}); });
it("is parsed in widget mode", () => { it("is parsed in widget mode", () => {
expect( expect(
getUrlParams( computeUrlParams(
"?userId=asd&widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo", "?userId=asd&widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo",
).userId, ).userId,
).toBe("asd"); ).toBe("asd");
@@ -156,12 +160,12 @@ describe("UrlParams", () => {
describe("deviceId", () => { describe("deviceId", () => {
it("is ignored in SPA mode", () => { it("is ignored in SPA mode", () => {
expect(getUrlParams("?deviceId=asd").deviceId).toBe(null); expect(computeUrlParams("?deviceId=asd").deviceId).toBe(null);
}); });
it("is parsed in widget mode", () => { it("is parsed in widget mode", () => {
expect( expect(
getUrlParams( computeUrlParams(
"?deviceId=asd&widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo", "?deviceId=asd&widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo",
).deviceId, ).deviceId,
).toBe("asd"); ).toBe("asd");
@@ -170,12 +174,12 @@ describe("UrlParams", () => {
describe("baseUrl", () => { describe("baseUrl", () => {
it("is ignored in SPA mode", () => { it("is ignored in SPA mode", () => {
expect(getUrlParams("?baseUrl=asd").baseUrl).toBe(null); expect(computeUrlParams("?baseUrl=asd").baseUrl).toBe(null);
}); });
it("is parsed in widget mode", () => { it("is parsed in widget mode", () => {
expect( expect(
getUrlParams( computeUrlParams(
"?baseUrl=asd&widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo", "?baseUrl=asd&widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo",
).baseUrl, ).baseUrl,
).toBe("asd"); ).toBe("asd");
@@ -185,28 +189,28 @@ describe("UrlParams", () => {
describe("viaServers", () => { describe("viaServers", () => {
it("is ignored in widget mode", () => { it("is ignored in widget mode", () => {
expect( expect(
getUrlParams( computeUrlParams(
"?viaServers=asd&widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo", "?viaServers=asd&widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo",
).viaServers, ).viaServers,
).toBe(null); ).toBe(null);
}); });
it("is parsed in SPA mode", () => { it("is parsed in SPA mode", () => {
expect(getUrlParams("?viaServers=asd").viaServers).toBe("asd"); expect(computeUrlParams("?viaServers=asd").viaServers).toBe("asd");
}); });
}); });
describe("homeserver", () => { describe("homeserver", () => {
it("is ignored in widget mode", () => { it("is ignored in widget mode", () => {
expect( expect(
getUrlParams( computeUrlParams(
"?homeserver=asd&widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo", "?homeserver=asd&widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo",
).homeserver, ).homeserver,
).toBe(null); ).toBe(null);
}); });
it("is parsed in SPA mode", () => { it("is parsed in SPA mode", () => {
expect(getUrlParams("?homeserver=asd").homeserver).toBe("asd"); expect(computeUrlParams("?homeserver=asd").homeserver).toBe("asd");
}); });
}); });
@@ -237,7 +241,7 @@ describe("UrlParams", () => {
controlledAudioDevices: platform === "desktop" ? false : true, controlledAudioDevices: platform === "desktop" ? false : true,
skipLobby: true, skipLobby: true,
returnToLobby: false, returnToLobby: false,
sendNotificationType: "notification", sendNotificationType: platform === "desktop" ? "notification" : "ring",
}); });
const joinExistingCallDefaults = (platform: string): object => ({ const joinExistingCallDefaults = (platform: string): object => ({
confineToRoom: true, confineToRoom: true,
@@ -252,24 +256,55 @@ describe("UrlParams", () => {
skipLobby: false, skipLobby: false,
returnToLobby: false, returnToLobby: false,
sendNotificationType: "notification", sendNotificationType: "notification",
defaultAudioEnabled: true,
defaultVideoEnabled: true,
}); });
it("use no-intent-defaults with unknown intent", () => { it("use no-intent-defaults with unknown intent", () => {
expect(getUrlParams()).toMatchObject(noIntentDefaults); expect(computeUrlParams()).toMatchObject(noIntentDefaults);
}); });
it("ignores intent if it is not a valid value", () => { it("ignores intent if it is not a valid value", () => {
expect(getUrlParams("?intent=foo")).toMatchObject(noIntentDefaults); expect(computeUrlParams("?intent=foo")).toMatchObject(noIntentDefaults);
}); });
it("accepts start_call", () => { it("accepts start_call", () => {
expect( expect(
getUrlParams("?intent=start_call&widgetId=1234&parentUrl=parent.org"), computeUrlParams(
"?intent=start_call&widgetId=1234&parentUrl=parent.org",
),
).toMatchObject(startNewCallDefaults("desktop")); ).toMatchObject(startNewCallDefaults("desktop"));
}); });
it("accepts start_call_dm mobile", () => {
vi.spyOn(PlatformMod, "platform", "get").mockReturnValue("android");
onTestFinished(() => {
vi.spyOn(PlatformMod, "platform", "get").mockReturnValue("desktop");
});
expect(
computeUrlParams(
"?intent=start_call_dm&widgetId=1234&parentUrl=parent.org",
),
).toMatchObject(startNewCallDefaults("android"));
});
it("accepts start_call_dm mobile and prioritizes overwritten params", () => {
vi.spyOn(PlatformMod, "platform", "get").mockReturnValue("android");
onTestFinished(() => {
vi.spyOn(PlatformMod, "platform", "get").mockReturnValue("desktop");
});
expect(
computeUrlParams(
"?intent=start_call_dm&widgetId=1234&parentUrl=parent.org&sendNotificationType=notification",
),
).toMatchObject({
...startNewCallDefaults("android"),
sendNotificationType: "notification",
});
});
it("accepts join_existing", () => { it("accepts join_existing", () => {
expect( expect(
getUrlParams( computeUrlParams(
"?intent=join_existing&widgetId=1234&parentUrl=parent.org", "?intent=join_existing&widgetId=1234&parentUrl=parent.org",
), ),
).toMatchObject(joinExistingCallDefaults("desktop")); ).toMatchObject(joinExistingCallDefaults("desktop"));
@@ -278,31 +313,55 @@ describe("UrlParams", () => {
describe("skipLobby", () => { describe("skipLobby", () => {
it("defaults to false", () => { it("defaults to false", () => {
expect(getUrlParams().skipLobby).toBe(false); expect(computeUrlParams().skipLobby).toBe(false);
}); });
it("defaults to false if intent is start_call in SPA mode", () => { it("defaults to false if intent is start_call in SPA mode", () => {
expect(getUrlParams("?intent=start_call").skipLobby).toBe(false); expect(computeUrlParams("?intent=start_call").skipLobby).toBe(false);
}); });
it("defaults to true if intent is start_call in widget mode", () => { it("defaults to true if intent is start_call in widget mode", () => {
expect( expect(
getUrlParams( computeUrlParams(
"?intent=start_call&widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo", "?intent=start_call&widgetId=12345&parentUrl=https%3A%2F%2Flocalhost%2Ffoo",
).skipLobby, ).skipLobby,
).toBe(true); ).toBe(true);
}); });
it("default to false if intent is join_existing", () => { it("default to false if intent is join_existing", () => {
expect(getUrlParams("?intent=join_existing").skipLobby).toBe(false); expect(computeUrlParams("?intent=join_existing").skipLobby).toBe(false);
}); });
}); });
describe("header", () => { describe("header", () => {
it("uses header if provided", () => { it("uses header if provided", () => {
expect(getUrlParams("?header=app_bar&hideHeader=true").header).toBe( expect(computeUrlParams("?header=app_bar&hideHeader=true").header).toBe(
"app_bar", "app_bar",
); );
expect(getUrlParams("?header=none&hideHeader=false").header).toBe("none"); expect(computeUrlParams("?header=none&hideHeader=false").header).toBe(
"none",
);
});
});
describe("getUrlParams", () => {
it("uses cached values", () => {
const spy = vi.spyOn(logger, "info");
// call get once
const params = getUrlParams("?header=app_bar&hideHeader=true", "");
// call get twice
expect(getUrlParams("?header=app_bar&hideHeader=true", "")).toBe(params);
// expect compute to only be called once
// it will only log when it is computing the values
expect(spy).toHaveBeenCalledExactlyOnceWith(
"UrlParams: final set of url params\n",
"intent:",
"unknown",
"\nproperties:",
expect.any(Object),
"configuration:",
expect.any(Object),
"intentAndPlatformDerivedConfiguration:",
{},
);
}); });
}); });
}); });

View File

@@ -322,8 +322,9 @@ let urlParamCache: {
hash?: string; hash?: string;
params?: UrlParams; params?: UrlParams;
} = {}; } = {};
/** /**
* Gets the app parameters for the current URL. * Gets the url params and loads them from a cache if already computed.
* @param search The URL search string * @param search The URL search string
* @param hash The URL hash * @param hash The URL hash
* @returns The app parameters encoded in the URL * @returns The app parameters encoded in the URL
@@ -331,18 +332,27 @@ let urlParamCache: {
export const getUrlParams = ( export const getUrlParams = (
search = window.location.search, search = window.location.search,
hash = window.location.hash, hash = window.location.hash,
/** Skipping the cache might be needed in tests, to allow recomputing based on mocked platform changes. */
skipCache = false,
): UrlParams => { ): UrlParams => {
// Only run the param configuration if we do not yet have it cached for this url.
if ( if (
urlParamCache.search === search && urlParamCache.search === search &&
urlParamCache.hash === hash && urlParamCache.hash === hash &&
urlParamCache.params && urlParamCache.params
!skipCache
) { ) {
return urlParamCache.params; return urlParamCache.params;
} }
const params = computeUrlParams(search, hash);
urlParamCache = { search, hash, params };
return params;
};
/**
* Gets the app parameters for the current URL.
* @param search The URL search string
* @param hash The URL hash
* @returns The app parameters encoded in the URL
*/
export const computeUrlParams = (search = "", hash = ""): UrlParams => {
const parser = new ParamParser(search, hash); const parser = new ParamParser(search, hash);
const fontScale = parseFloat(parser.getParam("fontScale") ?? ""); const fontScale = parseFloat(parser.getParam("fontScale") ?? "");
@@ -378,7 +388,7 @@ export const getUrlParams = (
controlledAudioDevices: platform === "desktop" ? false : true, controlledAudioDevices: platform === "desktop" ? false : true,
skipLobby: true, skipLobby: true,
returnToLobby: false, returnToLobby: false,
sendNotificationType: "notification" as RTCNotificationType, sendNotificationType: "notification",
autoLeaveWhenOthersLeft: false, autoLeaveWhenOthersLeft: false,
waitForCallPickup: false, waitForCallPickup: false,
}; };
@@ -392,6 +402,7 @@ export const getUrlParams = (
break; break;
case UserIntent.StartNewCallDM: case UserIntent.StartNewCallDM:
intentPreset.skipLobby = true; intentPreset.skipLobby = true;
intentPreset.sendNotificationType = "ring";
intentPreset.autoLeaveWhenOthersLeft = true; intentPreset.autoLeaveWhenOthersLeft = true;
intentPreset.waitForCallPickup = true; intentPreset.waitForCallPickup = true;
@@ -505,15 +516,12 @@ export const getUrlParams = (
intentAndPlatformDerivedConfiguration, intentAndPlatformDerivedConfiguration,
); );
const params = { return {
...properties, ...properties,
...intentPreset, ...intentPreset,
...pickBy(configuration, (v?: unknown) => v !== undefined), ...pickBy(configuration, (v?: unknown) => v !== undefined),
...intentAndPlatformDerivedConfiguration, ...intentAndPlatformDerivedConfiguration,
}; };
urlParamCache = { search, hash, params };
return params;
}; };
/** /**