Skip to content

Commit

Permalink
feature/disable camera & mute microphone (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
mstephen19 authored Jul 30, 2024
1 parent f9d48ec commit ac53ccf
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export const defaultTabData: TabData = {

export const defaultAddOns: AddOns = {
showLocationInfo: false,
hideCamera: false,
muteAudio: false,
};

export const MAX_MESSAGE_SEQUENCE_LENGTH = 75;
10 changes: 8 additions & 2 deletions src/content/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { tabDataStore } from '../storage';
import { addOnsStore, tabDataStore } from '../storage';
import { messages, tabData, config, addOns } from './cache';
import { commands } from './commands';
import { resetToIdle, sequenceLoop } from './sequence';
Expand All @@ -8,7 +8,7 @@ import { defaultAddOns, defaultConfig, defaultMessageSequence, defaultTabData }
import { elements, Tip } from './elements';
import { injectScripts } from './injected';
import { page } from './page';
import { PageEvent } from './injected/types';
import { PageCommand, PageEvent } from './injected/types';
import { getIPDetails, sanitizeIp } from '../utils';

async function main() {
Expand Down Expand Up @@ -39,6 +39,12 @@ async function main() {
await tabData.init(defaultTabData);
await addOns.init(defaultAddOns);

page.command(PageCommand.SetAddOnConfig, addOns.latest!);
addOnsStore.onChange((latest) => {
console.log('Change');
page.command(PageCommand.SetAddOnConfig, latest);
});

const startListener = async ({ detail: tabId }: CustomEvent<number>) => {
// If the login popup is open or there's an error, do nothing, but still listen for "Start" button clicks.
if (elements.loginPopup() || elements.errorPopup()) {
Expand Down
4 changes: 3 additions & 1 deletion src/content/injected/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import webrtc from './webrtc?script&module';
import media from './media?script&module';

const injectElement = <Tag extends keyof HTMLElementTagNameMap>(
parent: Element,
Expand Down Expand Up @@ -28,4 +29,5 @@ const injectScript = (src: string) =>
});
});

export const injectScripts = async () => void (await Promise.all([webrtc].map((script) => injectScript(chrome.runtime.getURL(script)))));
export const injectScripts = async () =>
void (await Promise.all([webrtc, media].map((script) => injectScript(chrome.runtime.getURL(script)))));
40 changes: 40 additions & 0 deletions src/content/injected/media.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { PageCommand, PageCommandDataMap } from './types';

const applyConfigToStream = (stream: MediaStream, config: PageCommandDataMap[PageCommand.SetAddOnConfig]) => {
if (!stream || !config) return;

stream.getVideoTracks().forEach((track) => (track.enabled = !config.hideCamera));
stream.getAudioTracks().forEach((track) => (track.enabled = !config.muteAudio));
};

const _getUserMedia = navigator.mediaDevices.getUserMedia;

const streamController = () => {
const config = { muteAudio: false, hideCamera: false };
let activeStream: MediaStream | null = null;

window.addEventListener('message', (event) => {
if (event.origin !== window.location.origin) return;
if (!event.data || event.data.type !== PageCommand.SetAddOnConfig) return;

const latestConfig = event.data.data as PageCommandDataMap[PageCommand.SetAddOnConfig];
for (const key in latestConfig) {
const configKey = key as keyof typeof config;
config[configKey] = latestConfig[configKey];
}

// When config is updated, reapply
if (activeStream) applyConfigToStream(activeStream, config);
});

navigator.mediaDevices.getUserMedia = async (constraints?: MediaStreamConstraints | undefined) => {
const stream = await _getUserMedia(constraints);

applyConfigToStream(stream, config);
activeStream = stream;

return stream;
};
};

streamController();
10 changes: 10 additions & 0 deletions src/content/injected/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import { AddOns } from '../../types';

export enum PageEvent {
PeerChange = 'peer_change',
}

export type PageEventDataMap = {
[PageEvent.PeerChange]: { address: string };
};

export enum PageCommand {
SetAddOnConfig = 'set_addon_config',
}

export type PageCommandDataMap = {
[PageCommand.SetAddOnConfig]: Pick<AddOns, 'muteAudio' | 'hideCamera'>;
};
6 changes: 5 additions & 1 deletion src/content/page.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TypedEventTarget } from '../utils';
import { PageEvent, PageEventDataMap } from './injected/types';
import { PageCommand, PageCommandDataMap, PageEvent, PageEventDataMap } from './injected/types';

const pageEventList = Object.values(PageEvent);

Expand All @@ -15,8 +15,12 @@ const pageEventRouter = () => {
events.dispatchEvent(new CustomEvent(event.data.type as PageEvent, { detail: event.data.data }));
});

const command = <Command extends PageCommand>(command: Command, data: PageCommandDataMap[Command]) =>
window.postMessage({ type: command, data });

return {
events,
command,
};
};

Expand Down
18 changes: 18 additions & 0 deletions src/popup/Accordions/AddOns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,24 @@ export const AddOns = () => {
label={addOns.showLocationInfo ? 'On' : 'Off'}
/>
</Box>

<Box display='flex' gap='10px'>
<ConfigName name='Hide camera' tip="Don't send your camera's video stream to connections." />

<FormControlLabel
control={<ConfigSwitch onChange={changeHandler('hideCamera')} checked={addOns.hideCamera} />}
label={addOns.hideCamera ? 'Hidden' : 'Off'}
/>
</Box>

<Box display='flex' gap='10px'>
<ConfigName name='Mute microphone' tip="Don't send your microphone's audio stream to connections." />

<FormControlLabel
control={<ConfigSwitch onChange={changeHandler('muteAudio')} checked={addOns.muteAudio} />}
label={addOns.muteAudio ? 'Muted' : 'Off'}
/>
</Box>
</Box>
);
};
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export type AppData = {

export type AddOns = {
showLocationInfo: boolean;
hideCamera: boolean;
muteAudio: boolean;
};

export type TabData = {
Expand Down
1 change: 1 addition & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export const raceWithEvent =
}
};

// todo: Use AbortController for more control over removing listeners
target.addEventListener(eventName, listener);

try {
Expand Down

0 comments on commit ac53ccf

Please sign in to comment.