Skip to content

Commit 5a4216a

Browse files
committed
Fix faulty update defer logic for good and refactor to promises
1 parent 1943fee commit 5a4216a

3 files changed

Lines changed: 182 additions & 139 deletions

File tree

extension/background.js

Lines changed: 158 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,6 @@ const timeFormat = {
77
hour12: true
88
}
99

10-
// Listen for extension updates
11-
chrome.runtime.onUpdateAvailable.addListener((details) => {
12-
console.log("Extension update available:", details.version)
13-
// Check if there is an active meeting
14-
chrome.storage.local.get(["meetingTabId"], function (result) {
15-
if (result.meetingTabId) {
16-
// There is an active meeting, defer the update
17-
chrome.storage.local.set({ deferredUpdateAvailableTimestamp: Date.now() }, function () {
18-
console.log("Deferred update flag set")
19-
})
20-
} else {
21-
// No active meeting, apply the update immediately
22-
console.log("No active meeting, applying update immediately")
23-
chrome.runtime.reload()
24-
}
25-
})
26-
})
2710

2811
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
2912
console.log(message.type)
@@ -39,15 +22,17 @@ chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
3922
}
4023

4124
if (message.type == "meeting_ended") {
42-
// Invalidate tab id since transcript is downloaded, prevents double downloading of transcript from tab closed event listener
43-
clearTabIdAndApplyUpdate()
44-
downloadAndPostWebhook()
45-
25+
downloadAndPostWebhook().finally(() => {
26+
// Invalidate tab id since transcript is downloaded, prevents double downloading of transcript from tab closed event listener
27+
clearTabIdAndApplyUpdate()
28+
})
4629
}
4730

4831
if (message.type == "download_transcript_at_index") {
4932
// Download the requested item
50-
downloadTranscript(message.index, false)
33+
downloadTranscript(message.index, false).then(() => {
34+
sendResponse({ success: true })
35+
})
5136
}
5237

5338
if (message.type == "retry_webhook_at_index") {
@@ -62,9 +47,14 @@ chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
6247
})
6348
}
6449

65-
if (message.type == "recover_last_transcript") {
66-
downloadAndPostWebhook()
67-
sendResponse({ message: "Recovery process started" })
50+
if (message.type == "recover_last_meeting") {
51+
downloadAndPostWebhook().then(() => {
52+
sendResponse({ success: true })
53+
}).catch((error) => {
54+
// Fails if transcript is empty or webhook request fails
55+
console.error("Recovery process failed:", error)
56+
sendResponse({ success: false, error: error.message })
57+
})
6858
}
6959
return true
7060
})
@@ -75,35 +65,76 @@ chrome.tabs.onRemoved.addListener(function (tabid) {
7565
if (tabid == data.meetingTabId) {
7666
console.log("Successfully intercepted tab close")
7767

78-
// Clearing meetingTabId to prevent misfires of onRemoved until next meeting actually starts
79-
clearTabIdAndApplyUpdate()
80-
downloadAndPostWebhook()
68+
downloadAndPostWebhook().finally(() => {
69+
// Clearing meetingTabId to prevent misfires of onRemoved until next meeting actually starts
70+
clearTabIdAndApplyUpdate()
71+
})
8172
}
8273
})
8374
})
8475

85-
// Download transcripts, post webhook if URL is enabled and available
76+
// Listen for extension updates
77+
chrome.runtime.onUpdateAvailable.addListener(() => {
78+
// Check if there is an active meeting
79+
chrome.storage.local.get(["meetingTabId"], function (result) {
80+
if (result.meetingTabId) {
81+
// There is an active meeting, defer the update
82+
chrome.storage.local.set({ isDeferredUpdatedAvailable: true }, function () {
83+
console.log("Deferred update flag set")
84+
})
85+
} else {
86+
// No active meeting, apply the update immediately. Meeting tab id is invalidated only post meeting operations are done, so no race conditions.
87+
console.log("No active meeting, applying update immediately")
88+
chrome.runtime.reload()
89+
}
90+
})
91+
})
92+
93+
// Download transcripts, post webhook if URL is enabled and available
94+
// Fails if transcript is empty or webhook request fails
8695
function downloadAndPostWebhook() {
87-
chrome.storage.local.get(["transcript", "chatMessages"], function (resultLocal) {
88-
// Check if at least one of transcript or chatMessages exist. To prevent downloading empty transcripts.
89-
if ((resultLocal.transcript != "") || (resultLocal.chatMessages != "")) {
90-
processTranscript().then(() => {
91-
chrome.storage.local.get(["meetings"], function (resultLocal) {
92-
chrome.storage.sync.get(["webhookUrl", "autoPostWebhookAfterMeeting"], function (resultSync) {
93-
// Download the last transcript
94-
const lastIndex = resultLocal.meetings.length - 1
95-
downloadTranscript(lastIndex, (resultSync.webhookUrl && resultSync.autoPostWebhookAfterMeeting) ? true : false)
96-
97-
// Post the last transcript to webhook if auto-post is enabled and available
98-
if (resultSync.autoPostWebhookAfterMeeting && resultSync.webhookUrl) {
99-
postTranscriptToWebhook(lastIndex).catch(error => {
100-
console.error("Webhook post failed:", error)
101-
})
102-
}
96+
return new Promise((resolve, reject) => {
97+
chrome.storage.local.get(["transcript", "chatMessages"], function (resultLocal) {
98+
// Check if at least one of transcript or chatMessages exist. To prevent downloading empty transcripts.
99+
if ((resultLocal.transcript != "") || (resultLocal.chatMessages != "")) {
100+
processTranscript().then(() => {
101+
chrome.storage.local.get(["meetings"], function (resultLocal) {
102+
chrome.storage.sync.get(["webhookUrl", "autoPostWebhookAfterMeeting"], function (resultSync) {
103+
// Create an array of promises to execute in parallel
104+
const promises = []
105+
106+
// Promise to download transcript
107+
const lastIndex = resultLocal.meetings.length - 1
108+
promises.push([
109+
downloadTranscript(
110+
lastIndex,
111+
resultSync.webhookUrl && resultSync.autoPostWebhookAfterMeeting ? true : false
112+
)
113+
])
114+
115+
// Promise to post webhook if enabled
116+
if (resultSync.autoPostWebhookAfterMeeting && resultSync.webhookUrl) {
117+
promises.push(postTranscriptToWebhook(lastIndex))
118+
}
119+
120+
// Execute all promises in parallel
121+
// First promise will always resolve, second one will fail if webhook request fails
122+
Promise.all(promises)
123+
.then(() => {
124+
resolve()
125+
})
126+
.catch(error => {
127+
console.error("Operation failed:", error)
128+
reject(error)
129+
})
130+
})
103131
})
104132
})
105-
})
106-
}
133+
}
134+
else {
135+
reject("Empty transcript and empty chatMessages")
136+
}
137+
})
107138
})
108139
}
109140

@@ -154,80 +185,84 @@ function processTranscript() {
154185

155186

156187
function downloadTranscript(index, webhookEnabled) {
157-
chrome.storage.local.get(["meetings"], function (result) {
158-
if (result.meetings && result.meetings[index]) {
159-
const meeting = result.meetings[index]
160-
161-
// Sanitise meeting title to prevent invalid file name errors
162-
// https://stackoverflow.com/a/78675894
163-
const invalidFilenameRegex = /[:?"*<>|~/\\\u{1}-\u{1f}\u{7f}\u{80}-\u{9f}\p{Cf}\p{Cn}]|^[.\u{0}\p{Zl}\p{Zp}\p{Zs}]|[.\u{0}\p{Zl}\p{Zp}\p{Zs}]$|^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(?=\.|$)/gui
164-
const sanitisedMeetingTitle = meeting.title.replaceAll(invalidFilenameRegex, "_")
165-
166-
// Format timestamp for human-readable filename
167-
const timestamp = new Date(meeting.meetingStartTimestamp)
168-
const formattedTimestamp = timestamp.toLocaleString("default", {
169-
year: "numeric",
170-
month: "2-digit",
171-
day: "2-digit",
172-
hour: "2-digit",
173-
minute: "2-digit",
174-
second: "2-digit",
175-
hour12: false
176-
}).replace(/[\/:]/g, "-")
177-
178-
const fileName = `TranscripTonic/Transcript-${sanitisedMeetingTitle} at ${formattedTimestamp}.txt`
179-
180-
181-
// Format transcript and chatMessages content
182-
let content = getTranscriptString(meeting.transcript)
183-
content += `\n\n---------------\nCHAT MESSAGES\n---------------\n\n`
184-
content += getChatMessagesString(meeting.chatMessages)
185-
186-
// Add branding
187-
content += "\n\n---------------\n"
188-
content += "Transcript saved using TranscripTonic Chrome extension (https://chromewebstore.google.com/detail/ciepnfnceimjehngolkijpnbappkkiag)"
189-
content += "\n---------------"
190-
191-
const blob = new Blob([content], { type: "text/plain" })
192-
193-
// Read the blob as a data URL
194-
const reader = new FileReader()
195-
196-
// Download once blob is read
197-
reader.onload = function (event) {
198-
const dataUrl = event.target.result
199-
200-
// Create a download with Chrome Download API
201-
chrome.downloads.download({
202-
url: dataUrl,
203-
filename: fileName,
204-
conflictAction: "uniquify"
205-
}).then(() => {
206-
console.log("Transcript downloaded")
207-
// Increment anonymous transcript generated count to a Google sheet
208-
fetch(`https://script.google.com/macros/s/AKfycbzUk-q3N8_BWjwE90g9HXs5im1pYFriydKi1m9FoxEmMrWhK8afrHSmYnwYcw6AkH14eg/exec?version=${chrome.runtime.getManifest().version}&webhookEnabled=${webhookEnabled}`, {
209-
mode: "no-cors"
210-
})
211-
}).catch((err) => {
212-
console.error(err)
188+
return new Promise((resolve) => {
189+
chrome.storage.local.get(["meetings"], function (result) {
190+
if (result.meetings && result.meetings[index]) {
191+
const meeting = result.meetings[index]
192+
193+
// Sanitise meeting title to prevent invalid file name errors
194+
// https://stackoverflow.com/a/78675894
195+
const invalidFilenameRegex = /[:?"*<>|~/\\\u{1}-\u{1f}\u{7f}\u{80}-\u{9f}\p{Cf}\p{Cn}]|^[.\u{0}\p{Zl}\p{Zp}\p{Zs}]|[.\u{0}\p{Zl}\p{Zp}\p{Zs}]$|^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(?=\.|$)/gui
196+
const sanitisedMeetingTitle = meeting.title.replaceAll(invalidFilenameRegex, "_")
197+
198+
// Format timestamp for human-readable filename
199+
const timestamp = new Date(meeting.meetingStartTimestamp)
200+
const formattedTimestamp = timestamp.toLocaleString("default", {
201+
year: "numeric",
202+
month: "2-digit",
203+
day: "2-digit",
204+
hour: "2-digit",
205+
minute: "2-digit",
206+
second: "2-digit",
207+
hour12: false
208+
}).replace(/[\/:]/g, "-")
209+
210+
const fileName = `TranscripTonic/Transcript-${sanitisedMeetingTitle} at ${formattedTimestamp}.txt`
211+
212+
213+
// Format transcript and chatMessages content
214+
let content = getTranscriptString(meeting.transcript)
215+
content += `\n\n---------------\nCHAT MESSAGES\n---------------\n\n`
216+
content += getChatMessagesString(meeting.chatMessages)
217+
218+
// Add branding
219+
content += "\n\n---------------\n"
220+
content += "Transcript saved using TranscripTonic Chrome extension (https://chromewebstore.google.com/detail/ciepnfnceimjehngolkijpnbappkkiag)"
221+
content += "\n---------------"
222+
223+
const blob = new Blob([content], { type: "text/plain" })
224+
225+
// Read the blob as a data URL
226+
const reader = new FileReader()
227+
228+
// Read the blob and download as text file
229+
reader.readAsDataURL(blob)
230+
231+
// Download once blob is read
232+
reader.onload = function (event) {
233+
const dataUrl = event.target.result
234+
235+
// Create a download with Chrome Download API
213236
chrome.downloads.download({
214237
url: dataUrl,
215-
filename: "TranscripTonic/Transcript.txt",
238+
filename: fileName,
216239
conflictAction: "uniquify"
240+
}).then(() => {
241+
console.log("Transcript downloaded")
242+
// Increment anonymous transcript generated count to a Google sheet
243+
fetch(`https://script.google.com/macros/s/AKfycbzUk-q3N8_BWjwE90g9HXs5im1pYFriydKi1m9FoxEmMrWhK8afrHSmYnwYcw6AkH14eg/exec?version=${chrome.runtime.getManifest().version}&webhookEnabled=${webhookEnabled}`, {
244+
mode: "no-cors"
245+
})
246+
resolve()
247+
}).catch((err) => {
248+
console.error(err)
249+
chrome.downloads.download({
250+
url: dataUrl,
251+
filename: "TranscripTonic/Transcript.txt",
252+
conflictAction: "uniquify"
253+
})
254+
console.log("Invalid file name. Transcript downloaded to TranscripTonic directory with simple file name.")
255+
// Logs anonymous errors to a Google sheet for swift debugging
256+
fetch(`https://script.google.com/macros/s/AKfycbxiyQSDmJuC2onXL7pKjXgELK1vA3aLGZL5_BLjzCp7fMoQ8opTzJBNfEHQX_QIzZ-j4Q/exec?version=${chrome.runtime.getManifest().version}&code=009&error=${encodeURIComponent(err)}`, { mode: "no-cors" })
257+
// Increment anonymous transcript generated count to a Google sheet
258+
fetch(`https://script.google.com/macros/s/AKfycbzUk-q3N8_BWjwE90g9HXs5im1pYFriydKi1m9FoxEmMrWhK8afrHSmYnwYcw6AkH14eg/exec?version=${chrome.runtime.getManifest().version}&webhookEnabled=${webhookEnabled}`, {
259+
mode: "no-cors"
260+
})
261+
resolve()
217262
})
218-
console.log("Invalid file name. Transcript downloaded to TranscripTonic directory with simple file name.")
219-
// Logs anonymous errors to a Google sheet for swift debugging
220-
fetch(`https://script.google.com/macros/s/AKfycbxiyQSDmJuC2onXL7pKjXgELK1vA3aLGZL5_BLjzCp7fMoQ8opTzJBNfEHQX_QIzZ-j4Q/exec?version=${chrome.runtime.getManifest().version}&code=009&error=${encodeURIComponent(err)}`, { mode: "no-cors" })
221-
// Increment anonymous transcript generated count to a Google sheet
222-
fetch(`https://script.google.com/macros/s/AKfycbzUk-q3N8_BWjwE90g9HXs5im1pYFriydKi1m9FoxEmMrWhK8afrHSmYnwYcw6AkH14eg/exec?version=${chrome.runtime.getManifest().version}&webhookEnabled=${webhookEnabled}`, {
223-
mode: "no-cors"
224-
})
225-
})
263+
}
226264
}
227-
228-
// Read the blob and download as text file
229-
reader.readAsDataURL(blob)
230-
}
265+
})
231266
})
232267
}
233268

@@ -345,19 +380,14 @@ function getChatMessagesString(chatMessages) {
345380
function clearTabIdAndApplyUpdate() {
346381
chrome.storage.local.set({ meetingTabId: null }, function () {
347382
console.log("Meeting tab id cleared for next meeting")
383+
348384
// Check if there's a deferred update
349-
chrome.storage.local.get(["deferredUpdateAvailableTimestamp"], function (result) {
350-
if (result.deferredUpdateAvailableTimestamp) {
351-
const timeSinceUpdateAvailable = Date.now() - result.deferredUpdateAvailableTimestamp
352-
const timeToWait = timeSinceUpdateAvailable > 10000 ? 0 : 10000 - timeSinceUpdateAvailable
353-
354-
console.log(`Applying deferred update in ${timeToWait}ms`)
355-
setTimeout(() => {
356-
console.log("Applying deferred update")
357-
chrome.storage.local.set({ deferredUpdateAvailableTimestamp: null }, function () {
358-
chrome.runtime.reload()
359-
})
360-
}, timeToWait)
385+
chrome.storage.local.get(["isDeferredUpdatedAvailable"], function (result) {
386+
if (result.isDeferredUpdatedAvailable) {
387+
console.log("Applying deferred update")
388+
chrome.storage.local.set({ isDeferredUpdatedAvailable: false }, function () {
389+
chrome.runtime.reload()
390+
})
361391
}
362392
})
363393
})

extension/content.js

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ async function checkExtensionStatus() {
576576
}
577577

578578
function recoverLastMeeting() {
579-
return new Promise((resolve) => {
579+
return new Promise((resolve, reject) => {
580580
chrome.storage.local.get(["meetings", "meetingStartTimestamp", "meetingStartTimeStamp"], function (result) {
581581
// Check if user ever attended a meeting
582582
// Backward compatible chrome storage variable. Old name "meetingStartTimeStamp".
@@ -588,22 +588,35 @@ function recoverLastMeeting() {
588588
if (result.meetingStartTimestamp !== meetingToDownload.meetingStartTimestamp) {
589589
// Silent failure if last meeting is an empty meeting
590590
chrome.runtime.sendMessage({
591-
type: "recover_last_transcript",
591+
type: "recover_last_meeting",
592592
}, function (response) {
593-
console.log(response)
594-
resolve()
595-
return
593+
if (response.success) {
594+
resolve()
595+
return
596+
}
597+
else {
598+
resolve("Could not recover. Last meeting was likely empty.")
599+
return
600+
}
596601
})
597602
}
603+
else {
604+
resolve("No recovery needed")
605+
}
598606
}
599607
// First meeting itself ended in a disaster. Need to recover that data, process and download it. Also handle recoveries of versions where "meetingStartTimeStamp" was used, because result.meetings will always be undefined in those versions.
600608
else {
601609
chrome.runtime.sendMessage({
602-
type: "recover_last_transcript",
610+
type: "recover_last_meeting",
603611
}, function (response) {
604-
console.log(response)
605-
resolve()
606-
return
612+
if (response.success) {
613+
resolve()
614+
return
615+
}
616+
else {
617+
reject("Could not recover. Last meeting was likely empty.")
618+
return
619+
}
607620
})
608621
}
609622
}

0 commit comments

Comments
 (0)