Skip to content

Commit

Permalink
Factor out common label-based toggle code
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisRegado committed Aug 6, 2022
1 parent cff2b9e commit 6509293
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 151 deletions.
75 changes: 3 additions & 72 deletions browser-extension/event_handlers/captions_event_handler.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,9 @@
class CaptionsEventHandler extends ToggleEventHandler {
class CaptionsEventHandler extends LabelBasedToggleEventHandler {

/**
* These are human-readable aria labels found on the Captions button, in
* different languages. The first element of each pair should be a substring
* of the label when captions are disabled, and the second when captions are enabled.
*
* Unlike most of our other controls, there don't seem to be unique `jsname`s
* for the captions button. If we discover another reliable way to identify caption
* controls that doesn't involve localized words, we should update this class
* to use that instead.
*/
static CaptionsButtonLabels = [
["Turn on captions", "Turn off captions"], // English
// ["Turn on captions (in german)", "Turn off captions (in german)"], // German
static ButtonLabels = [
["Turn on captions", "Turn off captions"], // English
];

static FlatCaptionsButtonLabels = CaptionsEventHandler.CaptionsButtonLabels.flat();

static MutedCaptionsButtonLabels = CaptionsEventHandler.CaptionsButtonLabels.map((languagePair) => {
return languagePair[0];
})

_isHandLabel = (ariaLabel) => {
return Boolean(
ariaLabel &&
CaptionsEventHandler.FlatCaptionsButtonLabels.find((l) => ariaLabel.includes(l))
);
}

_controlElementSelector = () => {
const elements = Array.from(document.querySelectorAll("[aria-label]"));
return elements.find((element) => {
const ariaLabel = element.getAttribute("aria-label");
return this._isHandLabel(ariaLabel);
});
}

_getControlElement = () => {
return this._controlElementSelector();
};

_isElementMuted = (element) => {
// "muted" means captions are off.
if (!element) {
return true;
}
const ariaLabel = element.getAttribute("aria-label");
return Boolean(CaptionsEventHandler.MutedCaptionsButtonLabels.find((handLabel) =>
ariaLabel.includes(handLabel)
));
}

_sendMuteState = () => {
this._sendSimpleMuteStateUpdate("captionsMutedState");
}
Expand All @@ -63,26 +16,4 @@ class CaptionsEventHandler extends ToggleEventHandler {
}
}

_handleControlChange = (mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === "attributes" && mutation.attributeName === "aria-label") {
const labelValue = mutation.target.attributes["aria-label"].value;
if (labelValue && this._isHandLabel(labelValue)) {
this._sendMuteState();
}
}
}
}

_registerMutationObserver = () => {
const observer = new MutationObserver(this._handleControlChange);
observer.observe(document.body, {
childList: false,
attributes: true,
attributeFilter: ["aria-label"],
attributeOldValue: true,
subtree: true,
});
}

}
81 changes: 2 additions & 79 deletions browser-extension/event_handlers/hand_event_handler.js
Original file line number Diff line number Diff line change
@@ -1,65 +1,10 @@
class HandEventHandler extends ToggleEventHandler {
class HandEventHandler extends LabelBasedToggleEventHandler {

/**
* These are human-readable aria labels found on the Raise Hand button, in
* different languages. The first element of each pair should be a substring
* of the label when the hand is lowered, and the second when the hand is raised.
*
* Unlike most of our other controls, there don't seem to be unique `jsname`s
* for the hand button. If we discover another reliable way to identify hand
* controls that doesn't involve localized words, we should update this class
* to use that instead.
*/
static HandButtonLabels = [
static ButtonLabels = [
["Raise hand", "Lower hand"], // English
["Melden", "Meldung zurückziehen"], // German
];

static FlatHandButtonLabels = HandEventHandler.HandButtonLabels.flat();

static MutedHandButtonLabels = HandEventHandler.HandButtonLabels.map((languagePair) => {
return languagePair[0];
})

_isHandLabel = (ariaLabel) => {
return Boolean(
ariaLabel &&
HandEventHandler.FlatHandButtonLabels.find((l) => ariaLabel.includes(l))
);
}

_controlElementSelector = () => {
const elements = Array.from(document.querySelectorAll("[aria-label]"));
return elements.find((element) => {
const ariaLabel = element.getAttribute("aria-label");
return this._isHandLabel(ariaLabel);
});
}

_getControlElement = () => {
/**
* As of the time this was written, not every Google account has the Raise
* Hand feature available, so we need to override ToggleEventHandler's
* default behavior of throwing an error. This effectively means that hand
* raising will fail silently. We can't be sure if we failed to find the
* button because the Meet UI changed, or if the user simply doesn't have
* the feature unlocked, and we don't want to throw errors on every state
* synchronization attempt.
*/
return this._controlElementSelector();
};

_isElementMuted = (element) => {
// "muted" means your hand is down.
if (!element) {
return true;
}
const ariaLabel = element.getAttribute("aria-label");
return Boolean(HandEventHandler.MutedHandButtonLabels.find((handLabel) =>
ariaLabel.includes(handLabel)
));
}

_sendMuteState = () => {
this._sendSimpleMuteStateUpdate("handMutedState");
}
Expand All @@ -72,26 +17,4 @@ class HandEventHandler extends ToggleEventHandler {
}
}

_handleControlChange = (mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === "attributes" && mutation.attributeName === "aria-label") {
const labelValue = mutation.target.attributes["aria-label"].value;
if (labelValue && this._isHandLabel(labelValue)) {
this._sendMuteState();
}
}
}
}

_registerMutationObserver = () => {
const observer = new MutationObserver(this._handleControlChange);
observer.observe(document.body, {
childList: false,
attributes: true,
attributeFilter: ["aria-label"],
attributeOldValue: true,
subtree: true,
});
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Some controls don't have unique `jsname`s, so we instead use human-readable
* aria labels (in different languages) found in the Meet UI to identify buttons.
*
* If we discover another reliable way to identify controls that doesn't involve
* localized words, we should switch over to that instead.
*/
class LabelBasedToggleEventHandler extends ToggleEventHandler {

/**
* Aria label strings representing the toggle button in both on and off states.
* The first element of each pair should be a substring of the label when the device is
* "muted"/off, and the second when the device is "unmuted"/on.
*/
static ButtonLabels = [];

_getFlatButtonLabels = () => this.constructor.ButtonLabels.flat();

_getMutedButtonLabels = () => this.constructor.ButtonLabels.map((languagePair) => {
return languagePair[0];
})

_isButtonLabel = (ariaLabel) => {
return Boolean(
ariaLabel &&
this._getFlatButtonLabels().find((l) => ariaLabel.includes(l))
);
}

_controlElementSelector = () => {
const elements = Array.from(document.querySelectorAll("[aria-label]"));
return elements.find((element) => {
const ariaLabel = element.getAttribute("aria-label");
return this._isButtonLabel(ariaLabel);
});
}

_getControlElement = () => {
/**
* Since our controls can only be located for certain languages, we have to
* treat buttons as optional. We don't want to throw errors on every state
* synchronization attempt.
*/
return this._controlElementSelector();
};

_isElementMuted = (element) => {
if (!element) {
return true;
}
const ariaLabel = element.getAttribute("aria-label");
return Boolean(this._getMutedButtonLabels().find((buttonLabel) =>
ariaLabel.includes(buttonLabel)
));
}

_handleControlChange = (mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === "attributes" && mutation.attributeName === "aria-label") {
const labelValue = mutation.target.attributes["aria-label"].value;
if (labelValue && this._isButtonLabel(labelValue)) {
this._sendMuteState();
}
}
}
}

_registerMutationObserver = () => {
const observer = new MutationObserver(this._handleControlChange);
observer.observe(document.body, {
childList: false,
attributes: true,
attributeFilter: ["aria-label"],
attributeOldValue: true,
subtree: true,
});
}

}
1 change: 1 addition & 0 deletions browser-extension/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"js": [
"event_handlers/base_event_handler.js",
"event_handlers/toggle_event_handler.js",
"event_handlers/label_based_toggle_event_handler.js",
"event_handlers/side_panel_event_handler.js",
"event_handlers/camera_event_handler.js",
"event_handlers/chat_event_handler.js",
Expand Down

0 comments on commit 6509293

Please sign in to comment.