Move Windows camera help into overlay
The privacy instructions no longer bloat the launcher card. A "Help" button in the navbar opens them in a native dialog (Esc, backdrop and button close it). The button only shows for Windows users, reusing the existing OS detection. Translations may contain <kbd>, <b> and <i> markup now. The renderer builds nodes from a tag whitelist instead of using innerHTML, so smuggled tags from i18n.json cannot execute. All help texts live under meetingPrivacySettings.windows; sibling keys for other operating systems can follow.
This commit is contained in:
1 parent
7961732323
commit
da2f6b5c25
4 files changed
+231
-109
No files matched your search
+89
-22
@@ -89,6 +89,46 @@ let translation = {};
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* "<b>", "<i>" and "<kbd>" are the only markup allowed
|
||||
* in translations (no nesting). Hand-built nodes instead
|
||||
* of innerHTML: a smuggled tag from i18n.json can never
|
||||
* execute.
|
||||
*
|
||||
* "Settings (<kbd>I</kbd>)" becomes
|
||||
*
|
||||
* Settings ("I")[text](kbd)
|
||||
*/
|
||||
const MARKUP_PATTERN = /<(b|i|kbd)>(.*?)<\/\1>/g;
|
||||
|
||||
function renderTranslation(element, text) {
|
||||
element.replaceChildren();
|
||||
|
||||
let lastIndex = 0;
|
||||
|
||||
for (const match of text.matchAll(MARKUP_PATTERN)) {
|
||||
/*
|
||||
* The pattern restricts the tag to the whitelist,
|
||||
* so createElement() cannot create foreign elements.
|
||||
*/
|
||||
element.append(
|
||||
document.createTextNode(
|
||||
text.slice(lastIndex, match.index)
|
||||
),
|
||||
Object.assign(
|
||||
document.createElement(match[1]),
|
||||
{ textContent: match[2] }
|
||||
)
|
||||
);
|
||||
|
||||
lastIndex = match.index + match[0].length;
|
||||
}
|
||||
|
||||
element.append(
|
||||
document.createTextNode(text.slice(lastIndex))
|
||||
);
|
||||
}
|
||||
|
||||
function applyTranslations() {
|
||||
document.documentElement.lang = currentLanguage;
|
||||
if (translation.pageTitle) {
|
||||
@@ -102,7 +142,7 @@ function applyTranslations() {
|
||||
const value = getTranslation(key);
|
||||
|
||||
if (value !== null) {
|
||||
element.textContent = value;
|
||||
renderTranslation(element, value);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -399,6 +439,22 @@ function applyLegalNoticeLink() {
|
||||
legalNoticeLink.classList.remove("hidden");
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* WINDOWS HELP BUTTON
|
||||
* ============================================================ */
|
||||
|
||||
/*
|
||||
* The camera/microphone help (opens windowsHelpOverlay)
|
||||
* only concerns Windows users.
|
||||
*/
|
||||
function applyWindowsHelpButton() {
|
||||
if (detectOperatingSystem() !== OS_NAMES.Windows) {
|
||||
return;
|
||||
}
|
||||
|
||||
windowsHelpButton.classList.remove("hidden");
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* DOM elements
|
||||
* ============================================================
|
||||
@@ -426,7 +482,6 @@ const iosLink = document.getElementById("iosLink");
|
||||
const androidLink = document.getElementById("androidLink");
|
||||
const createNewMeetingButton = document.getElementById("createNewMeetingButton");
|
||||
const createNewFromLauncherButton = document.getElementById("createNewFromLauncherButton");
|
||||
const privacyInfo = document.getElementById("privacyInfo");
|
||||
|
||||
/* Meeting Creator DOM elements */
|
||||
const meetingNameInput = document.getElementById("meetingName");
|
||||
@@ -451,6 +506,7 @@ const navbar = document.querySelector(".navbar");
|
||||
const navToggle = document.getElementById("navToggle");
|
||||
const navActions = document.getElementById("navActions");
|
||||
const legalNoticeLink = document.getElementById("legalNoticeLink");
|
||||
const windowsHelpButton = document.getElementById("windowsHelpButton");
|
||||
|
||||
/* Language menu DOM elements */
|
||||
const langMenu = document.getElementById("langMenu");
|
||||
@@ -480,6 +536,10 @@ const themeButton = document.getElementById("themeButton");
|
||||
const themeIconSun = document.getElementById("themeIconSun");
|
||||
const themeIconMoon = document.getElementById("themeIconMoon");
|
||||
|
||||
/* Windows help overlay DOM elements */
|
||||
const windowsHelpOverlay = document.getElementById("windowsHelpOverlay");
|
||||
const windowsHelpClose = document.getElementById("windowsHelpClose");
|
||||
|
||||
/* ============================================================
|
||||
* GET ROOM FROM QUERY-PARAMETER
|
||||
* ============================================================
|
||||
@@ -1401,26 +1461,9 @@ function cancelCreateMeeting() {
|
||||
*/
|
||||
|
||||
/* ============================================================
|
||||
* SHOW LAUNCHER
|
||||
* INITIALIZATION
|
||||
* ============================================================ */
|
||||
|
||||
/*
|
||||
* Camera/microphone hints (meetingPrivacySettings in
|
||||
* i18n.json) only matter on Windows – and only with
|
||||
* loaded translations, otherwise the box would be empty.
|
||||
*/
|
||||
function showPrivacyInfo() {
|
||||
if (detectOperatingSystem() !== OS_NAMES.Windows) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (getTranslation("meetingPrivacySettings.title") === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
privacyInfo.classList.remove("hidden");
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
const room = getRoom();
|
||||
|
||||
@@ -1482,8 +1525,6 @@ function initialize() {
|
||||
*/
|
||||
launcher.classList.remove("hidden");
|
||||
|
||||
showPrivacyInfo();
|
||||
|
||||
/*
|
||||
* Hide the "Create New Meeting" button when a valid room is specified
|
||||
*/
|
||||
@@ -1604,6 +1645,31 @@ navToggle.addEventListener(
|
||||
toggleNavMenu
|
||||
);
|
||||
|
||||
/* Windows help events */
|
||||
windowsHelpButton.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
windowsHelpOverlay.showModal();
|
||||
}
|
||||
);
|
||||
|
||||
windowsHelpClose.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
windowsHelpOverlay.close();
|
||||
}
|
||||
);
|
||||
|
||||
/* Clicks on the backdrop close the overlay */
|
||||
windowsHelpOverlay.addEventListener(
|
||||
"click",
|
||||
(event) => {
|
||||
if (event.target === windowsHelpOverlay) {
|
||||
windowsHelpOverlay.close();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/*
|
||||
* Follow the system if no choice is saved.
|
||||
*/
|
||||
@@ -1664,6 +1730,7 @@ async function start() {
|
||||
applyNextcloudButton();
|
||||
applyAdhocButton();
|
||||
applyLegalNoticeLink();
|
||||
applyWindowsHelpButton();
|
||||
|
||||
await loadTranslations();
|
||||
|
||||
|
||||
Reference in new issue
Block a user