Skip to content

Commit 73a3a96

Browse files
committed
Wait for elements before clicking and consuming
1 parent e291e7d commit 73a3a96

5 files changed

Lines changed: 78 additions & 68 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Simple Google Meet transcripts. Private and open source.
33

44
![marquee-large](/assets/marquee-large.png)
55

6-
Extension status: 🟢 OPERATIONAL (v3.1.5)
6+
Extension status: 🟢 OPERATIONAL (v3.1.6)
77

88
<br />
99
<br />

extension-unpacked.zip

122 Bytes
Binary file not shown.

extension/background.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ chrome.tabs.onRemoved.addListener(function (tabId) {
119119

120120
// Prevent misfires of onRemoved until next meeting. Also prevents available update from being applied, during meeting post processing.
121121
chrome.storage.local.set({ meetingTabId: "processing" }, function () {
122-
console.log("Meeting tab id cleared for next meeting")
122+
console.log("Meeting tab id set to processing meeting")
123123

124124
processLastMeeting().finally(() => {
125125
clearTabIdAndApplyUpdate()

extension/content.js

Lines changed: 75 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,7 @@ function meetingRoutines(uiType) {
152152

153153

154154
//*********** MEETING START ROUTINES **********//
155-
// Pick up meeting name after a delay, since Google meet updates meeting name after a delay
156-
setTimeout(() => updateMeetingTitle(), 5000)
155+
updateMeetingTitle()
157156

158157
/** @type {MutationObserver} */
159158
let transcriptObserver
@@ -162,41 +161,43 @@ function meetingRoutines(uiType) {
162161

163162
// **** REGISTER TRANSCRIPT LISTENER **** //
164163
try {
165-
// CRITICAL DOM DEPENDENCY
166-
const captionsButton = selectElements(captionsIconData.selector, captionsIconData.text)[0]
167-
168-
// Click captions icon for non manual operation modes. Async operation.
169-
chrome.storage.sync.get(["operationMode"], function (resultSyncUntyped) {
170-
const resultSync = /** @type {ResultSync} */ (resultSyncUntyped)
171-
if (resultSync.operationMode === "manual")
172-
console.log("Manual mode selected, leaving transcript off")
173-
else
174-
captionsButton.click()
175-
})
164+
waitForElement(captionsIconData.selector, captionsIconData.text).then(() => {
165+
// CRITICAL DOM DEPENDENCY
166+
const captionsButton = selectElements(captionsIconData.selector, captionsIconData.text)[0]
167+
168+
// Click captions icon for non manual operation modes. Async operation.
169+
chrome.storage.sync.get(["operationMode"], function (resultSyncUntyped) {
170+
const resultSync = /** @type {ResultSync} */ (resultSyncUntyped)
171+
if (resultSync.operationMode === "manual")
172+
console.log("Manual mode selected, leaving transcript off")
173+
else
174+
captionsButton.click()
175+
})
176176

177-
// CRITICAL DOM DEPENDENCY. Grab the transcript element. This element is present, irrespective of captions ON/OFF, so this executes independent of operation mode.
178-
let transcriptTargetNode = document.querySelector(`div[role="region"][tabindex="0"]`)
179-
// For old captions UI
180-
if (!transcriptTargetNode) {
181-
transcriptTargetNode = document.querySelector(".a4cQT")
182-
canUseAriaBasedTranscriptSelector = false
183-
}
177+
// CRITICAL DOM DEPENDENCY. Grab the transcript element. This element is present, irrespective of captions ON/OFF, so this executes independent of operation mode.
178+
let transcriptTargetNode = document.querySelector(`div[role="region"][tabindex="0"]`)
179+
// For old captions UI
180+
if (!transcriptTargetNode) {
181+
transcriptTargetNode = document.querySelector(".a4cQT")
182+
canUseAriaBasedTranscriptSelector = false
183+
}
184184

185-
if (transcriptTargetNode) {
186-
// Attempt to dim down the transcript
187-
canUseAriaBasedTranscriptSelector
188-
? transcriptTargetNode.setAttribute("style", "opacity:0.2")
189-
: transcriptTargetNode.children[1].setAttribute("style", "opacity:0.2")
185+
if (transcriptTargetNode) {
186+
// Attempt to dim down the transcript
187+
canUseAriaBasedTranscriptSelector
188+
? transcriptTargetNode.setAttribute("style", "opacity:0.2")
189+
: transcriptTargetNode.children[1].setAttribute("style", "opacity:0.2")
190190

191-
// Create transcript observer instance linked to the callback function. Registered irrespective of operation mode, so that any visible transcript can be picked up during the meeting, independent of the operation mode.
192-
transcriptObserver = new MutationObserver(transcriptMutationCallback)
191+
// Create transcript observer instance linked to the callback function. Registered irrespective of operation mode, so that any visible transcript can be picked up during the meeting, independent of the operation mode.
192+
transcriptObserver = new MutationObserver(transcriptMutationCallback)
193193

194-
// Start observing the transcript element and chat messages element for configured mutations
195-
transcriptObserver.observe(transcriptTargetNode, mutationConfig)
196-
}
197-
else {
198-
throw new Error("Transcript element not found in DOM")
199-
}
194+
// Start observing the transcript element and chat messages element for configured mutations
195+
transcriptObserver.observe(transcriptTargetNode, mutationConfig)
196+
}
197+
else {
198+
throw new Error("Transcript element not found in DOM")
199+
}
200+
})
200201
} catch (err) {
201202
console.error(err)
202203
isTranscriptDomErrorCaptured = true
@@ -207,32 +208,35 @@ function meetingRoutines(uiType) {
207208

208209
// **** REGISTER CHAT MESSAGES LISTENER **** //
209210
try {
210-
const chatMessagesButton = selectElements(".google-symbols", "chat")[0]
211-
// Force open chat messages to make the required DOM to appear. Otherwise, the required chatMessages DOM element is not available.
212-
chatMessagesButton.click()
213-
214-
// Allow DOM to be updated, close chat messages and then register chatMessage mutation observer
215-
waitForElement(`div[aria-live="polite"].Ge9Kpc`).then(() => {
211+
// Wait for chat icon to be visible. When user is waiting in meeting lobbing for someone to let them in, the call end icon is visible, but the chat icon is still not visible.
212+
waitForElement(".google-symbols", "chat").then(() => {
213+
const chatMessagesButton = selectElements(".google-symbols", "chat")[0]
214+
// Force open chat messages to make the required DOM to appear. Otherwise, the required chatMessages DOM element is not available.
216215
chatMessagesButton.click()
217-
// CRITICAL DOM DEPENDENCY. Grab the chat messages element. This element is present, irrespective of chat ON/OFF, once it appears for this first time.
218-
try {
219-
const chatMessagesTargetNode = document.querySelector(`div[aria-live="polite"].Ge9Kpc`)
220-
221-
// Create chat messages observer instance linked to the callback function. Registered irrespective of operation mode.
222-
if (chatMessagesTargetNode) {
223-
chatMessagesObserver = new MutationObserver(chatMessagesMutationCallback)
224-
chatMessagesObserver.observe(chatMessagesTargetNode, mutationConfig)
225-
}
226-
else {
227-
throw new Error("Chat messages element not found in DOM")
228-
}
229-
} catch (err) {
230-
console.error(err)
231-
isChatMessagesDomErrorCaptured = true
232-
showNotification(extensionStatusJSON_bug)
233216

234-
logError("002", err)
235-
}
217+
// Allow DOM to be updated, close chat messages and then register chatMessage mutation observer
218+
waitForElement(`div[aria-live="polite"].Ge9Kpc`).then(() => {
219+
chatMessagesButton.click()
220+
// CRITICAL DOM DEPENDENCY. Grab the chat messages element. This element is present, irrespective of chat ON/OFF, once it appears for this first time.
221+
try {
222+
const chatMessagesTargetNode = document.querySelector(`div[aria-live="polite"].Ge9Kpc`)
223+
224+
// Create chat messages observer instance linked to the callback function. Registered irrespective of operation mode.
225+
if (chatMessagesTargetNode) {
226+
chatMessagesObserver = new MutationObserver(chatMessagesMutationCallback)
227+
chatMessagesObserver.observe(chatMessagesTargetNode, mutationConfig)
228+
}
229+
else {
230+
throw new Error("Chat messages element not found in DOM")
231+
}
232+
} catch (err) {
233+
console.error(err)
234+
isChatMessagesDomErrorCaptured = true
235+
showNotification(extensionStatusJSON_bug)
236+
237+
logError("002", err)
238+
}
239+
})
236240
})
237241
} catch (err) {
238242
console.error(err)
@@ -542,21 +546,27 @@ function pulseStatus() {
542546
// Grabs updated meeting title, if available
543547
function updateMeetingTitle() {
544548
try {
545-
// NON CRITICAL DOM DEPENDENCY
546-
const meetingTitleElement = document.querySelector(".u6vdEc")
547-
if (meetingTitleElement?.textContent) {
548-
meetingTitle = meetingTitleElement.textContent
549-
overWriteChromeStorage(["meetingTitle"], false)
550-
} else {
551-
throw new Error("Meeting title element not found in DOM")
552-
}
549+
waitForElement(".u6vdEc").then(() => {
550+
// Pick up meeting name after a delay, since Google meet updates meeting name after a delay
551+
setTimeout(() => {
552+
// NON CRITICAL DOM DEPENDENCY
553+
const meetingTitleElement = document.querySelector(".u6vdEc")
554+
if (meetingTitleElement?.textContent) {
555+
meetingTitle = meetingTitleElement.textContent
556+
overWriteChromeStorage(["meetingTitle"], false)
557+
} else {
558+
throw new Error("Meeting title element not found in DOM")
559+
}
560+
}, 5000)
561+
})
553562
} catch (err) {
554563
console.error(err)
555564

556565
if (!hasMeetingEnded) {
557566
logError("007", err)
558567
}
559568
}
569+
560570
}
561571

562572
// Returns all elements of the specified selector type and specified textContent. Return array contains the actual element as well as all the parents.

extension/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 3,
33
"name": "TranscripTonic",
4-
"version": "3.1.5",
4+
"version": "3.1.6",
55
"description": "Simple Google Meet transcripts. Private and open source.",
66
"action": {
77
"default_icon": "icon.png",

0 commit comments

Comments
 (0)