spot: refactor types, update wxt core, use global browser

This commit is contained in:
nick-delirium 2024-09-05 10:47:15 +02:00
parent a2b0366267
commit 4563f90eee
No known key found for this signature in database
GPG key ID: 93ABD695DF5FDBA0
8 changed files with 269 additions and 213 deletions

5
spot/declarations.d.ts vendored Normal file
View file

@ -0,0 +1,5 @@
declare global {
function defineBackground(cb: () => any): any
const chrome: typeof import('wxt/browser')['browser']
const browser: typeof import('wxt/browser')['browser']
}

View file

@ -1,7 +1,8 @@
import { WebRequest } from "webextension-polyfill"; import { WebRequest } from "webextension-polyfill";
export default defineBackground(() => { export default defineBackground(() => {
const CHECK_INT = 60 * 1000; const CHECK_INT = 60 * 1000;
const PING_INT = 30 * 1000 const PING_INT = 30 * 1000;
const messages = { const messages = {
popup: { popup: {
from: { from: {
@ -45,7 +46,7 @@ export default defineBackground(() => {
micStatus: "content:mic-status", micStatus: "content:mic-status",
unmount: "content:unmount", unmount: "content:unmount",
notification: "notif:display", notification: "notif:display",
updateErrorEvents: "content:error-events" updateErrorEvents: "content:error-events",
}, },
}, },
injected: { injected: {
@ -140,12 +141,12 @@ export default defineBackground(() => {
function setJWTToken(token: string) { function setJWTToken(token: string) {
jwtToken = token; jwtToken = token;
if (token && token.length) { if (token && token.length) {
chrome.storage.local.set({ jwtToken: token }); void browser.storage.local.set({ jwtToken: token });
void browser.runtime.sendMessage({ void browser.runtime.sendMessage({
type: messages.popup.loginExist, type: messages.popup.loginExist,
}); });
} else { } else {
chrome.storage.local.remove("jwtToken"); void browser.storage.local.remove("jwtToken");
void browser.runtime.sendMessage({ void browser.runtime.sendMessage({
type: messages.popup.to.noLogin, type: messages.popup.to.noLogin,
}); });
@ -165,7 +166,7 @@ export default defineBackground(() => {
let slackChannels: { name: string; webhookId: number }[] = []; let slackChannels: { name: string; webhookId: number }[] = [];
const refreshToken = async () => { const refreshToken = async () => {
const data = await chrome.storage.local.get(["jwtToken", "settings"]) const data = await browser.storage.local.get(["jwtToken", "settings"]);
if (!data.settings) { if (!data.settings) {
return; return;
} }
@ -185,7 +186,7 @@ export default defineBackground(() => {
}, },
}); });
if (!resp.ok) { if (!resp.ok) {
chrome.storage.local.remove("jwtToken"); void browser.storage.local.remove("jwtToken");
setJWTToken(""); setJWTToken("");
void browser.runtime.sendMessage({ void browser.runtime.sendMessage({
type: messages.popup.to.noLogin, type: messages.popup.to.noLogin,
@ -216,47 +217,47 @@ export default defineBackground(() => {
}; };
let refreshInt: any; let refreshInt: any;
let pingInt: any; let pingInt: any;
chrome.storage.local.get(["jwtToken", "settings"]).then(async (data: any) => { browser.storage.local
if (!data.settings) { .get(["jwtToken", "settings"])
chrome.storage.local.set({ settings }); .then(async (data: any) => {
return; if (!data.settings) {
} void browser.storage.local.set({ settings });
const newObj = Object.assign(settings, data.settings); return;
settings = newObj; }
settings = Object.assign(settings, data.settings);
if (!data.jwtToken) { if (!data.jwtToken) {
console.error("No JWT token found in storage"); console.error("No JWT token found in storage");
void browser.runtime.sendMessage({ void browser.runtime.sendMessage({
type: messages.popup.to.noLogin, type: messages.popup.to.noLogin,
}); });
return; return;
} }
const url = safeApiUrl(`${data.settings.ingestPoint}/api`); const url = safeApiUrl(`${data.settings.ingestPoint}/api`);
const ok = await refreshToken(); const ok = await refreshToken();
if (ok) { if (ok) {
fetchSlackChannels(data.jwtToken, url).catch((e) => { fetchSlackChannels(data.jwtToken, url).catch((e) => {
console.error(e); console.error(e);
void refreshToken();
});
if (!refreshInt) {
refreshInt = setInterval(() => {
void refreshToken(); void refreshToken();
}, CHECK_INT); });
}
if (!pingInt) { if (!refreshInt) {
pingInt = setInterval(() => { refreshInt = setInterval(() => {
void pingJWT(); void refreshToken();
}, PING_INT) }, CHECK_INT);
}
if (!pingInt) {
pingInt = setInterval(() => {
void pingJWT();
}, PING_INT);
}
} }
} });
});
async function pingJWT(): Promise<void> { async function pingJWT(): Promise<void> {
const data = await chrome.storage.local.get(["jwtToken", "settings"]) const data = await browser.storage.local.get(["jwtToken", "settings"]);
console.log(data, 'ping')
if (!data.settings) { if (!data.settings) {
return; return;
} }
@ -275,16 +276,17 @@ export default defineBackground(() => {
headers: { headers: {
Authorization: `Bearer ${jwtToken}`, Authorization: `Bearer ${jwtToken}`,
}, },
}) });
if (!r.ok) { if (!r.ok) {
void refreshToken() void refreshToken();
} }
} catch (e) { } catch (e) {
void refreshToken() void refreshToken();
} }
} }
let lastReq: Record<string, any> | null = null; let lastReq: Record<string, any> | null = null;
// @ts-ignore
browser.runtime.onMessage.addListener((request, sender, respond) => { browser.runtime.onMessage.addListener((request, sender, respond) => {
if (request.type === messages.content.from.contentReady) { if (request.type === messages.content.from.contentReady) {
if (sender?.tab?.id) { if (sender?.tab?.id) {
@ -303,7 +305,7 @@ export default defineBackground(() => {
activeTabId: null, activeTabId: null,
area: null, area: null,
recording: REC_STATE.stopped, recording: REC_STATE.stopped,
audioPerm: request.permissions ? request.mic ? 2 : 1 : 0, audioPerm: request.permissions ? (request.mic ? 2 : 1) : 0,
}; };
if (request.area === "tab") { if (request.area === "tab") {
browser.tabs browser.tabs
@ -321,7 +323,7 @@ export default defineBackground(() => {
area: request.area, area: request.area,
mic: request.mic, mic: request.mic,
audioId: request.selectedAudioDevice, audioId: request.selectedAudioDevice,
audioPerm: request.permissions ? request.mic ? 2 : 1 : 0, audioPerm: request.permissions ? (request.mic ? 2 : 1) : 0,
}); });
}); });
} else { } else {
@ -330,13 +332,13 @@ export default defineBackground(() => {
area: request.area, area: request.area,
mic: request.mic, mic: request.mic,
audioId: request.selectedAudioDevice, audioId: request.selectedAudioDevice,
audioPerm: request.permissions ? request.mic ? 2 : 1 : 0, audioPerm: request.permissions ? (request.mic ? 2 : 1) : 0,
}); });
} }
} }
if (request.type === messages.content.from.countEnd) { if (request.type === messages.content.from.countEnd) {
if (!jwtToken) { if (!jwtToken) {
chrome.storage.local.get("jwtToken", (data: any) => { browser.storage.local.get("jwtToken").then((data: any) => {
if (!data.jwtToken) { if (!data.jwtToken) {
console.error("No JWT token found"); console.error("No JWT token found");
void browser.runtime.sendMessage({ void browser.runtime.sendMessage({
@ -369,6 +371,7 @@ export default defineBackground(() => {
settings.consoleLogs, settings.consoleLogs,
() => recordingState.recording, () => recordingState.recording,
); );
// @ts-ignore this is false positive
respond(true); respond(true);
} }
if (!recordingState.activeTabId) { if (!recordingState.activeTabId) {
@ -407,6 +410,7 @@ export default defineBackground(() => {
onStop = hook; onStop = hook;
}, },
).then(() => { ).then(() => {
// @ts-ignore this is false positive
respond(true); respond(true);
}); });
return true; return true;
@ -433,7 +437,8 @@ export default defineBackground(() => {
}); });
} }
if (request.type === messages.content.from.checkNewTab) { if (request.type === messages.content.from.checkNewTab) {
chrome.storage.local.get("settings", (data: any) => { browser.storage.local.get("settings").then((data: any) => {
// @ts-ignore this is false positive
respond(Boolean(data.settings.openInNewTab)); respond(Boolean(data.settings.openInNewTab));
}); });
@ -462,12 +467,12 @@ export default defineBackground(() => {
} }
if (request.type === messages.content.from.setLoginToken) { if (request.type === messages.content.from.setLoginToken) {
setJWTToken(request.token); setJWTToken(request.token);
chrome.storage.local.get("settings", (data: any) => { browser.storage.local.get("settings").then((data: any) => {
if (!data.settings) { if (!data.settings) {
chrome.storage.local.set({ settings }); void browser.storage.local.set({ settings });
return; return;
} }
if (!refreshInt) { if (!refreshInt) {
refreshInt = setInterval(() => { refreshInt = setInterval(() => {
void refreshToken(); void refreshToken();
}, CHECK_INT); }, CHECK_INT);
@ -475,7 +480,7 @@ export default defineBackground(() => {
if (!pingInt) { if (!pingInt) {
pingInt = setInterval(() => { pingInt = setInterval(() => {
void pingJWT(); void pingJWT();
}, PING_INT) }, PING_INT);
} }
}); });
} }
@ -484,7 +489,7 @@ export default defineBackground(() => {
clearInterval(refreshInt); clearInterval(refreshInt);
} }
if (pingInt) { if (pingInt) {
clearInterval(pingInt) clearInterval(pingInt);
} }
setJWTToken(""); setJWTToken("");
} }
@ -497,14 +502,15 @@ export default defineBackground(() => {
if (request.type === messages.popup.from.updateSettings) { if (request.type === messages.popup.from.updateSettings) {
const updatedObject = Object.assign(settings, request.settings); const updatedObject = Object.assign(settings, request.settings);
settings = updatedObject; settings = updatedObject;
if ('ingestPoint' in request.settings) { if ("ingestPoint" in request.settings) {
setJWTToken("") setJWTToken("");
} }
chrome.storage.local.set({ settings: updatedObject }); void browser.storage.local.set({ settings: updatedObject });
} }
if (request.type === messages.content.from.checkRecStatus) { if (request.type === messages.content.from.checkRecStatus) {
const id = request.tabId; const id = request.tabId;
if (recordingState.area === "tab" && id !== recordingState.activeTabId) { if (recordingState.area === "tab" && id !== recordingState.activeTabId) {
// @ts-ignore this is false positive
respond({ status: false }); respond({ status: false });
} else { } else {
browser.runtime browser.runtime
@ -513,6 +519,7 @@ export default defineBackground(() => {
target: "offscreen", target: "offscreen",
}) })
.then((r) => { .then((r) => {
// @ts-ignore this is false positive
respond({ ...r, state: recordingState.recording }); respond({ ...r, state: recordingState.recording });
}); });
} }
@ -586,31 +593,36 @@ export default defineBackground(() => {
activeTabId: null, activeTabId: null,
area: null, area: null,
recording: REC_STATE.stopped, recording: REC_STATE.stopped,
audioPerm: lastReq.permissions, audioPerm: lastReq!.permissions,
}; };
void sendToActiveTab({ void sendToActiveTab({
type: "content:mount", type: "content:mount",
area: lastReq.area, area: lastReq!.area,
mic: lastReq.mic, mic: lastReq!.mic,
audioId: lastReq.selectedAudioDevice, audioId: lastReq!.selectedAudioDevice,
audioPerm: lastReq.permissions, audioPerm: lastReq!.permissions,
}); });
} }
if (request.type === messages.content.from.getErrorEvents) { if (request.type === messages.content.from.getErrorEvents) {
const logs = finalSpotObj.logs.filter( const logs = finalSpotObj.logs
(log) => log.level === "error", .filter((log) => log.level === "error")
).map(l => ({ title: 'JS Error', time: (l.time - finalSpotObj.startTs)/1000 })) .map((l) => ({
const network = finalSpotObj.network.filter( title: "JS Error",
(net) => net.statusCode >= 400 || net.error, time: (l.time - finalSpotObj.startTs) / 1000,
).map(n => ({ title: 'Network Error', time: (n.time - finalSpotObj.startTs)/1000 })) }));
const network = finalSpotObj.network
.filter((net) => net.statusCode >= 400 || net.error)
.map((n) => ({
title: "Network Error",
time: (n.time - finalSpotObj.startTs) / 1000,
}));
const errorData = [...logs, ...network] const errorData = [...logs, ...network].sort((a, b) => a.time - b.time);
.sort((a, b) => a.time - b.time)
void sendToActiveTab({ void sendToActiveTab({
type: messages.content.to.updateErrorEvents, type: messages.content.to.updateErrorEvents,
errorData, errorData,
}) });
} }
if (request.type === "ort:stop") { if (request.type === "ort:stop") {
browser.runtime browser.runtime
@ -621,8 +633,10 @@ export default defineBackground(() => {
.then((r) => { .then((r) => {
if (r.status === "full") { if (r.status === "full") {
finalSpotObj.duration = r.duration; finalSpotObj.duration = r.duration;
// @ts-ignore this is false positive
respond(r); respond(r);
} else { } else {
// @ts-ignore this is false positive
respond({ status: r.status }); respond({ status: r.status });
} }
}); });
@ -633,7 +647,7 @@ export default defineBackground(() => {
} }
if (request.type === "offscr:video-data-chunk") { if (request.type === "offscr:video-data-chunk") {
finalSpotObj.duration = request.duration; finalSpotObj.duration = request.duration;
sendToActiveTab({ void sendToActiveTab({
type: "content:video-chunk", type: "content:video-chunk",
data: request.data, data: request.data,
index: request.index, index: request.index,
@ -666,7 +680,7 @@ export default defineBackground(() => {
type: "content:mic-status", type: "content:mic-status",
micStatus: micStatus === "on", micStatus: micStatus === "on",
}); });
chrome.runtime.sendMessage({ void browser.runtime.sendMessage({
type: messages.popup.to.micStatus, type: messages.popup.to.micStatus,
status: false, status: false,
}); });
@ -681,7 +695,7 @@ export default defineBackground(() => {
type: "content:mic-status", type: "content:mic-status",
micStatus: micStatus === "on", micStatus: micStatus === "on",
}); });
chrome.runtime.sendMessage({ void browser.runtime.sendMessage({
type: messages.popup.to.micStatus, type: messages.popup.to.micStatus,
status: true, status: true,
}); });
@ -712,7 +726,7 @@ export default defineBackground(() => {
finalVideoBase64 += request.part; finalVideoBase64 += request.part;
finalReady = request.index === request.total - 1; finalReady = request.index === request.total - 1;
const getPlatformData = async () => { const getPlatformData = async () => {
const vendor = await chrome.runtime.getPlatformInfo(); const vendor = await browser.runtime.getPlatformInfo();
const platform = `${vendor.os} ${vendor.arch}`; const platform = `${vendor.os} ${vendor.arch}`;
return { platform }; return { platform };
}; };
@ -782,102 +796,105 @@ export default defineBackground(() => {
const ingestUrl = safeApiUrl(settings.ingestPoint); const ingestUrl = safeApiUrl(settings.ingestPoint);
const dataUrl = `${ingestUrl}/spot/v1/spots`; const dataUrl = `${ingestUrl}/spot/v1/spots`;
refreshToken().then((r) => { refreshToken()
if (!r) { .then((r) => {
void sendToActiveTab({ if (!r) {
type: messages.content.to.notification,
message: `Error saving Spot: couldn't get active login`,
});
}
fetch(dataUrl, {
method: "POST",
body: JSON.stringify(dataObj),
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${jwtToken}`,
},
})
.then((r) => {
if (r.ok) {
return r.json();
} else {
if (r.status === 401) {
throw new Error(
"Not authorized or no permissions to create Spot",
);
}
}
})
.then(async (resp) => {
recordingState = {
activeTabId: null,
area: null,
recording: REC_STATE.stopped,
};
// id of spot, mobURL - for events, videoURL - for video
if (!resp || !resp.id) {
return sendToActiveTab({
type: messages.content.to.notification,
message: "Couldn't save Spot",
});
}
const { id, mobURL, videoURL } = resp;
const link = settings.ingestPoint.includes("api.openreplay.com")
? "https://app.openreplay.com"
: settings.ingestPoint;
chrome.tabs.create({
url: `${link}/view-spot/${id}`,
active: settings.openInNewTab,
});
void sendToActiveTab({
type: "content:spot-saved",
url: `${link}/view-spot/${id}`,
});
const blob = base64ToBlob(videoData);
const mPromise = fetch(mobURL, {
method: "PUT",
body: JSON.stringify(mobData),
headers: {
"Content-Type": "application/json",
},
});
const vPromise = fetch(videoURL, {
method: "PUT",
headers: {
"Content-Type": "video/webm",
},
body: blob,
});
Promise.all([mPromise, vPromise])
.then(async (r) => {
const uploadedUrl = `${safeApiUrl(settings.ingestPoint)}/spot/v1/spots/${id}/uploaded`;
void fetch(uploadedUrl, {
method: "POST",
headers: {
Authorization: `Bearer ${jwtToken}`,
},
});
})
.catch(console.error);
})
.catch((e) => {
console.error(e);
void sendToActiveTab({ void sendToActiveTab({
type: messages.content.to.notification, type: messages.content.to.notification,
message: `Error saving Spot: ${e.message}`, message: `Error saving Spot: couldn't get active login`,
}); });
}
fetch(dataUrl, {
method: "POST",
body: JSON.stringify(dataObj),
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${jwtToken}`,
},
}) })
.finally(() => { .then((r) => {
finalSpotObj = defaultSpotObj; if (r.ok) {
}); return r.json();
}) } else {
.catch(e => { if (r.status === 401) {
throw new Error(
"Not authorized or no permissions to create Spot",
);
}
}
})
.then(async (resp) => {
recordingState = {
activeTabId: null,
area: null,
recording: REC_STATE.stopped,
};
// id of spot, mobURL - for events, videoURL - for video
if (!resp || !resp.id) {
return sendToActiveTab({
type: messages.content.to.notification,
message: "Couldn't save Spot",
});
}
const { id, mobURL, videoURL } = resp;
const link = settings.ingestPoint.includes(
"api.openreplay.com",
)
? "https://app.openreplay.com"
: settings.ingestPoint;
void browser.tabs.create({
url: `${link}/view-spot/${id}`,
active: settings.openInNewTab,
});
void sendToActiveTab({
type: "content:spot-saved",
url: `${link}/view-spot/${id}`,
});
const blob = base64ToBlob(videoData);
const mPromise = fetch(mobURL, {
method: "PUT",
body: JSON.stringify(mobData),
headers: {
"Content-Type": "application/json",
},
});
const vPromise = fetch(videoURL, {
method: "PUT",
headers: {
"Content-Type": "video/webm",
},
body: blob,
});
Promise.all([mPromise, vPromise])
.then(async () => {
const uploadedUrl = `${safeApiUrl(settings.ingestPoint)}/spot/v1/spots/${id}/uploaded`;
void fetch(uploadedUrl, {
method: "POST",
headers: {
Authorization: `Bearer ${jwtToken}`,
},
});
})
.catch(console.error);
})
.catch((e) => {
console.error(e);
void sendToActiveTab({
type: messages.content.to.notification,
message: `Error saving Spot: ${e.message}`,
});
})
.finally(() => {
finalSpotObj = defaultSpotObj;
});
})
.catch((e) => {
void sendToActiveTab({ void sendToActiveTab({
type: messages.content.to.notification, type: messages.content.to.notification,
message: `Error saving Spot: ${e.message}`, message: `Error saving Spot: ${e.message}`,
}); });
}) });
}); });
} }
@ -897,11 +914,17 @@ export default defineBackground(() => {
}); });
void initializeOffscreenDocument(); void initializeOffscreenDocument();
type TrackedRequest = type TrackedRequest = {
statusCode: number;
requestHeaders: Record<string, string>;
responseHeaders: Record<string, string>;
} & (
| WebRequest.OnBeforeRequestDetailsType | WebRequest.OnBeforeRequestDetailsType
| WebRequest.OnBeforeSendHeadersDetailsType | WebRequest.OnBeforeSendHeadersDetailsType
| WebRequest.OnCompletedDetailsType | WebRequest.OnCompletedDetailsType
| WebRequest.OnErrorOccurredDetailsType; | WebRequest.OnErrorOccurredDetailsType
| WebRequest.OnResponseStartedDetailsType
);
interface SpotNetworkRequest { interface SpotNetworkRequest {
encodedBodySize: number; encodedBodySize: number;
@ -922,20 +945,6 @@ export default defineBackground(() => {
startTs: number; startTs: number;
duration: number; duration: number;
})[] = []; })[] = [];
const processedRequests: SpotNetworkRequest[] = [];
function parseBody(body: any): string {
if (body instanceof ArrayBuffer) {
return "Binary ArrayBuffer omitted";
}
if (body instanceof Blob) {
return "Binary Blob omitted";
}
try {
return JSON.stringify(body);
} catch (e) {
return "Failed to parse request body";
}
}
function filterHeaders(headers: Record<string, string>) { function filterHeaders(headers: Record<string, string>) {
const filteredHeaders: Record<string, string> = {}; const filteredHeaders: Record<string, string> = {};
const privateHs = [ const privateHs = [
@ -1065,7 +1074,6 @@ export default defineBackground(() => {
}; };
function startTrackingNetwork() { function startTrackingNetwork() {
rawRequests.length = 0; rawRequests.length = 0;
processedRequests.length = 0;
browser.webRequest.onBeforeRequest.addListener( browser.webRequest.onBeforeRequest.addListener(
// @ts-ignore // @ts-ignore
trackOnBefore, trackOnBefore,
@ -1159,8 +1167,7 @@ export default defineBackground(() => {
[key: string]: any; [key: string]: any;
}) { }) {
try { try {
const resp = await browser.runtime.sendMessage(message); return await browser.runtime.sendMessage(message);
return resp;
} catch (e) { } catch (e) {
console.error("Sending to offscreen", e); console.error("Sending to offscreen", e);
} }
@ -1366,7 +1373,7 @@ export default defineBackground(() => {
const decodeJwt = (jwt: string): any => { const decodeJwt = (jwt: string): any => {
const base64Url = jwt.split(".")[1]; const base64Url = jwt.split(".")[1];
if (!base64Url) { if (!base64Url) {
return { exp: 0 } return { exp: 0 };
} }
const base64 = base64Url.replace("-", "+").replace("_", "/"); const base64 = base64Url.replace("-", "+").replace("_", "/");
return JSON.parse(atob(base64)); return JSON.parse(atob(base64));
@ -1383,7 +1390,7 @@ export default defineBackground(() => {
return request.statusCode; return request.statusCode;
} }
if (request.error) { if (request.error) {
return 0 return 0;
} }
return 200; return 200;
} }

View file

@ -29,7 +29,7 @@ interface IControlsBox {
getInitState: () => string; getInitState: () => string;
onRestart: () => void; onRestart: () => void;
getErrorEvents: () => Promise<any>; getErrorEvents: () => Promise<any>;
getAudioPerm: () => number, getAudioPerm: () => 0 | 1 | 2;
} }
function ControlsBox({ function ControlsBox({
@ -59,24 +59,37 @@ function ControlsBox({
const onTimerEnd = async (proceed?: boolean) => { const onTimerEnd = async (proceed?: boolean) => {
if (!proceed) { if (!proceed) {
onClose(false); onClose(false);
return changeState(STATES.idle) return changeState(STATES.idle);
} }
let tries = 0;
// changeState(STATES.idle);
await callRecording(); await callRecording();
let int = setInterval(() => { let int = setInterval(() => {
const state = getInitState(); const state = getInitState();
tries++;
if (tries > 200) {
clearInterval(int);
changeState(STATES.idle);
}
if (state !== "count") { if (state !== "count") {
clearInterval(int); clearInterval(int);
changeState(STATES.recording); changeState(STATES.recording);
} }
}, 100); }, 50);
}; };
return ( return (
<div class={"controls"}> <div class={"controls"}>
{boxState() === STATES.saving ? ( {boxState() === STATES.saving ? (
<SavingControls getErrorEvents={getErrorEvents} getVideoData={getVideoData} onClose={onClose} /> <SavingControls
getErrorEvents={getErrorEvents}
getVideoData={getVideoData}
onClose={onClose}
/>
) : null}
{boxState() === STATES.count ? (
<Countdown getAudioPerm={getAudioPerm} onEnd={onTimerEnd} />
) : null} ) : null}
{boxState() === STATES.count ? <Countdown getAudioPerm={getAudioPerm} onEnd={onTimerEnd} /> : null}
{boxState() === STATES.recording ? ( {boxState() === STATES.recording ? (
<RecordingControls <RecordingControls
getAudioPerm={getAudioPerm} getAudioPerm={getAudioPerm}

View file

@ -2,7 +2,7 @@ import { createSignal, onCleanup, onMount } from "solid-js";
function Countdown(props: { function Countdown(props: {
onEnd: (proceed?: boolean) => void; onEnd: (proceed?: boolean) => void;
getAudioPerm: () => number; getAudioPerm: () => 0 | 1 | 2;
}) { }) {
const [count, setCount] = createSignal(3); const [count, setCount] = createSignal(3);
@ -39,8 +39,8 @@ function Countdown(props: {
const audioPrompt = { const audioPrompt = {
0: "Microphone permission isn't granted yet.", 0: "Microphone permission isn't granted yet.",
1: "Microphone access is enabled. Unmute anytime to add voice over.", 1: "Microphone access is enabled. Unmute anytime to add voice over.",
2: "Microphone is enabled." 2: "Microphone is enabled.",
} };
return ( return (
<div class="modal-overlay"> <div class="modal-overlay">

View file

@ -14,7 +14,7 @@ interface ISavingControls {
comment?: string; comment?: string;
useHook?: boolean; useHook?: boolean;
thumbnail?: string; thumbnail?: string;
crop?: [number, number]; crop: [number, number] | null;
}, },
) => void; ) => void;
getVideoData: () => Promise<any>; getVideoData: () => Promise<any>;
@ -51,7 +51,9 @@ function SavingControls({
const [endPos, setEndPos] = createSignal(100); const [endPos, setEndPos] = createSignal(100);
const [dragging, setDragging] = createSignal<string | null>(null); const [dragging, setDragging] = createSignal<string | null>(null);
const [isTyping, setIsTyping] = createSignal(false); const [isTyping, setIsTyping] = createSignal(false);
const [errorEvents, setErrorEvents] = createSignal([]); const [errorEvents, setErrorEvents] = createSignal<
{ title: string; time: number }[]
>([]);
createEffect(() => { createEffect(() => {
setTrimBounds([0, 0]); setTrimBounds([0, 0]);
@ -226,7 +228,7 @@ function SavingControls({
} }
setDuration(videoDuration); setDuration(videoDuration);
setErrorEvents( setErrorEvents(
errorEvents.filter((ev: { time: number }) => ev.time < videoDuration), errorEvents().filter((ev: { time: number }) => ev.time < videoDuration),
); );
void generateThumbnail(); void generateThumbnail();
}; };

View file

@ -21,8 +21,8 @@ export default defineContentScript({
position: "inline", position: "inline",
anchor: "body", anchor: "body",
append: "first", append: "first",
onMount: (container,s,host) => { onMount: (container, s, host) => {
Object.assign(host.style, { visibility: 'visible', display: 'block' }); Object.assign(host.style, { visibility: "visible", display: "block" });
return render( return render(
() => ( () => (
@ -66,8 +66,9 @@ export default defineContentScript({
}); });
}; };
// no perm - muted - unmuted // no perm - muted - unmuted
let audioPerm = 0; type AudioPermState = 0 | 1 | 2;
const getAudioPerm = () => audioPerm let audioPerm: AudioPermState = 0;
const getAudioPerm = (): AudioPermState => audioPerm;
let clockStart = 0; let clockStart = 0;
let recState = "stopped"; let recState = "stopped";
const getClockStart = () => { const getClockStart = () => {
@ -186,7 +187,7 @@ export default defineContentScript({
return; return;
} }
const { name, comment, useHook, thumbnail, crop, blob } = spotObj; const { name, comment, useHook, thumbnail, crop, blob } = spotObj;
const videoData = await convertBlobToBase64(blob); const videoData = await convertBlobToBase64(blob!);
const resolution = `${window.screen.width}x${window.screen.height}`; const resolution = `${window.screen.width}x${window.screen.height}`;
const browserVersion = getChromeFullVersion(); const browserVersion = getChromeFullVersion();
const spot = { const spot = {
@ -302,6 +303,7 @@ export default defineContentScript({
} }
void browser.runtime.sendMessage({ type: "ort:content-ready" }); void browser.runtime.sendMessage({ type: "ort:content-ready" });
// @ts-ignore false positive
browser.runtime.onMessage.addListener((message: any, resp) => { browser.runtime.onMessage.addListener((message: any, resp) => {
if (message.type === "content:mount") { if (message.type === "content:mount") {
if (recState === "count") return; if (recState === "count") return;

View file

@ -31,6 +31,6 @@
"@wxt-dev/module-solid": "^1.1.2", "@wxt-dev/module-solid": "^1.1.2",
"daisyui": "^4.12.10", "daisyui": "^4.12.10",
"typescript": "^5.4.5", "typescript": "^5.4.5",
"wxt": "0.19.8" "wxt": "0.19.9"
} }
} }

View file

@ -941,11 +941,36 @@
dependencies: dependencies:
"@babel/types" "^7.20.7" "@babel/types" "^7.20.7"
"@types/chrome@^0.0.269":
version "0.0.269"
resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.269.tgz#5bc2a536bb72a3332119742f31b42b5250d39905"
integrity sha512-vF7x8YywnhXX2F06njQ/OE7a3Qeful43C5GUOsUksXWk89WoSFUU3iLeZW8lDpVO9atm8iZIEiLQTRC3H7NOXQ==
dependencies:
"@types/filesystem" "*"
"@types/har-format" "*"
"@types/estree@1.0.5", "@types/estree@^1.0.0": "@types/estree@1.0.5", "@types/estree@^1.0.0":
version "1.0.5" version "1.0.5"
resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz" resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz"
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
"@types/filesystem@*":
version "0.0.36"
resolved "https://registry.yarnpkg.com/@types/filesystem/-/filesystem-0.0.36.tgz#7227c2d76bfed1b21819db310816c7821d303857"
integrity sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==
dependencies:
"@types/filewriter" "*"
"@types/filewriter@*":
version "0.0.33"
resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.33.tgz#d9d611db9d9cd99ae4e458de420eeb64ad604ea8"
integrity sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==
"@types/har-format@*":
version "1.2.15"
resolved "https://registry.yarnpkg.com/@types/har-format/-/har-format-1.2.15.tgz#f352493638c2f89d706438a19a9eb300b493b506"
integrity sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA==
"@types/http-cache-semantics@^4.0.2": "@types/http-cache-semantics@^4.0.2":
version "4.0.4" version "4.0.4"
resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz" resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz"
@ -2100,9 +2125,9 @@ execa@^8.0.1:
signal-exit "^4.1.0" signal-exit "^4.1.0"
strip-final-newline "^3.0.0" strip-final-newline "^3.0.0"
execa@^9.3.0: execa@^9.3.1:
version "9.3.1" version "9.3.1"
resolved "https://registry.npmjs.org/execa/-/execa-9.3.1.tgz" resolved "https://registry.yarnpkg.com/execa/-/execa-9.3.1.tgz#09c86ab4dc2ef3de6d34f6568f4bad76ded4fded"
integrity sha512-gdhefCCNy/8tpH/2+ajP9IQc14vXchNdd0weyzSJEFURhRMGncQ+zKFxwjAufIewPEJm9BPOaJnvg2UtlH2gPQ== integrity sha512-gdhefCCNy/8tpH/2+ajP9IQc14vXchNdd0weyzSJEFURhRMGncQ+zKFxwjAufIewPEJm9BPOaJnvg2UtlH2gPQ==
dependencies: dependencies:
"@sindresorhus/merge-streams" "^4.0.0" "@sindresorhus/merge-streams" "^4.0.0"
@ -3855,9 +3880,9 @@ ora@^6.3.1:
strip-ansi "^7.0.1" strip-ansi "^7.0.1"
wcwidth "^1.0.1" wcwidth "^1.0.1"
ora@^8.0.1: ora@^8.1.0:
version "8.1.0" version "8.1.0"
resolved "https://registry.npmjs.org/ora/-/ora-8.1.0.tgz" resolved "https://registry.yarnpkg.com/ora/-/ora-8.1.0.tgz#c3db2f9f83a2bec9e8ab71fe3b9ae234d65ca3a8"
integrity sha512-GQEkNkH/GHOhPFXcqZs3IDahXEQcQxsSjEkK4KvEEST4t7eNzoMjxTzef+EZ+JluDEV+Raoi3WQ2CflnRdSVnQ== integrity sha512-GQEkNkH/GHOhPFXcqZs3IDahXEQcQxsSjEkK4KvEEST4t7eNzoMjxTzef+EZ+JluDEV+Raoi3WQ2CflnRdSVnQ==
dependencies: dependencies:
chalk "^5.3.0" chalk "^5.3.0"
@ -4981,9 +5006,9 @@ undici-types@~6.19.2:
resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz" resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz"
integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
unimport@^3.9.1: unimport@^3.11.1:
version "3.11.1" version "3.11.1"
resolved "https://registry.npmjs.org/unimport/-/unimport-3.11.1.tgz" resolved "https://registry.yarnpkg.com/unimport/-/unimport-3.11.1.tgz#b750eb69fbacf481e000829a79eb9da19919f33e"
integrity sha512-DuB1Uoq01LrrXTScxnwOoMSlTXxyKcULguFxbLrMDFcE/CO0ZWHpEiyhovN0mycPt7K6luAHe8laqvwvuoeUPg== integrity sha512-DuB1Uoq01LrrXTScxnwOoMSlTXxyKcULguFxbLrMDFcE/CO0ZWHpEiyhovN0mycPt7K6luAHe8laqvwvuoeUPg==
dependencies: dependencies:
"@rollup/pluginutils" "^5.1.0" "@rollup/pluginutils" "^5.1.0"
@ -5306,12 +5331,13 @@ ws@8.18.0:
resolved "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz" resolved "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz"
integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==
wxt@0.19.8: wxt@0.19.9:
version "0.19.8" version "0.19.9"
resolved "https://registry.yarnpkg.com/wxt/-/wxt-0.19.8.tgz#b07b98d95a34e5e2d916c3a6a53520e0d0b1b66d" resolved "https://registry.yarnpkg.com/wxt/-/wxt-0.19.9.tgz#b8f7f838cab00d66f4ee22f483c49ad8f6527af8"
integrity sha512-Z3LxRuM8rTZ9VOZnQ2DjtRz3uLpZpFzTfBjRVgEOHv1kf/7sRVdeKCY4pUmtimgfR2/NGclYAbCfAVTYQJg0GA== integrity sha512-XUbF4JNyx2jTDpXwx2c/esaJcUD2Dr482C2GGenkGRMH2UnerzOIchGCtaa1hb2U8eAed7Akda0yRoMJU0uxUw==
dependencies: dependencies:
"@aklinker1/rollup-plugin-visualizer" "5.12.0" "@aklinker1/rollup-plugin-visualizer" "5.12.0"
"@types/chrome" "^0.0.269"
"@types/webextension-polyfill" "^0.10.7" "@types/webextension-polyfill" "^0.10.7"
"@webext-core/fake-browser" "^1.3.1" "@webext-core/fake-browser" "^1.3.1"
"@webext-core/isolated-element" "^1.1.2" "@webext-core/isolated-element" "^1.1.2"
@ -5325,7 +5351,7 @@ wxt@0.19.8:
defu "^6.1.4" defu "^6.1.4"
dequal "^2.0.3" dequal "^2.0.3"
esbuild "^0.23.0" esbuild "^0.23.0"
execa "^9.3.0" execa "^9.3.1"
fast-glob "^3.3.2" fast-glob "^3.3.2"
filesize "^10.1.4" filesize "^10.1.4"
fs-extra "^11.2.0" fs-extra "^11.2.0"
@ -5344,11 +5370,12 @@ wxt@0.19.8:
nypm "^0.3.9" nypm "^0.3.9"
ohash "^1.1.3" ohash "^1.1.3"
open "^10.1.0" open "^10.1.0"
ora "^8.0.1" ora "^8.1.0"
picocolors "^1.0.1" picocolors "^1.0.1"
prompts "^2.4.2" prompts "^2.4.2"
publish-browser-extension "^2.1.3" publish-browser-extension "^2.1.3"
unimport "^3.9.1" scule "^1.3.0"
unimport "^3.11.1"
vite "^5.3.5" vite "^5.3.5"
vite-node "^2.0.4" vite-node "^2.0.4"
web-ext-run "^0.2.1" web-ext-run "^0.2.1"