Skip to content

Commit

Permalink
Merge pull request #152 from swfz/feature/sakura-comment
Browse files Browse the repository at this point in the history
さくらコメント設定機能
  • Loading branch information
swfz authored Sep 21, 2023
2 parents 32472bf + 79f8691 commit 4aa2c8e
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 50 deletions.
2 changes: 2 additions & 0 deletions content_script/presenter_subscribe.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
console.log('loaded google slide comment stream');

const subscribeComments = (observeElement, sendResponse) => {
const broadcastChannel = new BroadcastChannel('comment_channel');

Expand Down
39 changes: 39 additions & 0 deletions content_script/sakura.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
console.log('loaded google slide comment stream');

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
console.log('subscribe plant comment when page changed started');

const broadcastChannel = new BroadcastChannel('plant_comment_channel');

const handleEvent = (event) => {
chrome.storage.sync.get(['config'], ({ config }) => {
if(config.plant) {
const textarea = document.querySelector<HTMLTextAreaElement>('.punch-ask-question-submit-question-dialog-question-textarea');
if (textarea === null) {
return
}
textarea.click();

const anonimity = document.querySelector<HTMLInputElement>('.docs-material-gm-labeled-checkbox-checkbox');
if (anonimity?.ariaChecked === 'false') {
anonimity.click();
}

const inputEvent = new Event('input', { 'bubbles': true, 'cancelable': true });
textarea.value = event.data;
textarea.dispatchEvent(inputEvent);

const button = document.querySelector('.punch-ask-question-submit-button')

const mousedownEvent = new MouseEvent("mousedown", { bubbles: true, cancelable: true, view: window, button: 0 });
button?.dispatchEvent(mousedownEvent);

const mouseupEvent = new MouseEvent("mouseup", { bubbles: true, cancelable: true, view: window, button: 0 });
button?.dispatchEvent(mouseupEvent);
}
});
};

broadcastChannel.onmessage = handleEvent;
sendResponse({ screenType: 'usertool', message: 'A listener has been added to the usertool.' });
});
38 changes: 38 additions & 0 deletions content_script/stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,43 @@ const clapElementStyle = (bottom, right) => {
};
};

const addSubscribePageNumber = () => {
const broadcastChannel = new BroadcastChannel('plant_comment_channel');
const iframeElement: HTMLIFrameElement = document.querySelector('.punch-present-iframe');

if (iframeElement === null) {
return;
}
const observeElement = iframeElement.contentWindow.document.querySelector('.docs-material-menu-button-flat-default-caption');

if (observeElement === null) {
console.log('not exist observe element');
return;
}

const observer = new MutationObserver(function (records) {
const added = records.at(-1)?.addedNodes[0]?.textContent;
const removed = records[0]?.removedNodes[0]?.textContent;

if (added && removed && added > removed) {
chrome.storage.sync.get(['sakura'], ({sakura}) => {
const plantCommentRows = sakura[added];

if (plantCommentRows !== undefined) {
plantCommentRows.forEach((commentRow) => {
setTimeout(() => {
broadcastChannel.postMessage(commentRow.comment);
}, commentRow.seconds * 1000);
});
}
})
}
});

observer.observe(observeElement, { subtree: true, childList: true });
}


chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
const iframeElement: HTMLIFrameElement = document.querySelector('.punch-present-iframe');

Expand Down Expand Up @@ -127,5 +164,6 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
broadcastChannel.onmessage = handleEvent;
});

addSubscribePageNumber();
sendResponse({ screenType: 'slide', message: 'A listener has been added to the SLIDE side.' });
});
6 changes: 6 additions & 0 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@
"matches": ["https://docs.google.com/presentation/d/*/edit"],
"match_about_blank": true,
"js": ["content_script/stream.ts", "content_script/presenter_subscribe.ts"]
},
{
"matches": ["https://docs.google.com/presentation/d/e/*/askquestion?*"],
"match_about_blank": true,
"js": ["content_script/sakura.ts"]
}
],
"options_page": "options.html",
"action": {
"default_popup": "index.html",
"default_icon": {
Expand Down
50 changes: 50 additions & 0 deletions options.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="button.css">
</head>
<body>
<div>
<h2>Comment setting for preparation</h2>
</div>
<div>
<p>
This function is used to play a predefined comment at a predetermined timing.
</p>
<p>
The key is the slide number
</p>
<p>
When you transition to the target slide number, a `comment` message is automatically posted after `seconds` seconds of each line
</p>
<p>
To use this feature, the extension must also Start on the screen where the audience posts comments in the User Tools
</p>
</div>

<hr />
<div>Configuration Structure(e.g.)</div>
<pre>
<code>
{
"3": [
{"seconds": 0.5, "comment": "looks good"},
{"seconds": 1, "comment": "cooooooooooool!!!!"}
],
"4": [
{"seconds": 2, "comment": "wow"}
]
}
</code>
</pre>
<hr />

<div id="optionsList">
</div>
<textarea cols="100" rows="20" id="sakura"></textarea>
<br />
<button class="save" id="save">Save</button>
</body>
<script src="options.js"></script>
</html>
20 changes: 20 additions & 0 deletions options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
function options() {

const textarea = document.getElementById('sakura');
chrome.storage.sync.get(['sakura'], ({sakura}) => {
if (sakura) {
textarea.value = JSON.stringify(sakura, null, 2);
}
});

const handleSave = function(event) {
const sakuraOption = document.querySelector('textarea[id="sakura"]').value;
const json = JSON.parse(sakuraOption);
chrome.storage.sync.set({sakura: json});
}

const saveButton = document.getElementById('save');
saveButton.addEventListener('click', handleSave);
}

options();
33 changes: 20 additions & 13 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,6 @@
color: white;
}

.App-link {
color: #61dafb;
}

@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

button {
font-size: calc(10px + 2vmin);
}
Expand All @@ -64,3 +51,23 @@ label {
height: 100px;
vertical-align: top;
}

.pseudo-table {
display: table;
width: 100%;
border-collapse: collapse;
}

.pseudo-row {
display: table-row;
}

.pseudo-cell {
display: table-cell;
padding: 5px 1px;
border: 1px solid #999;
}

.pseudo-cell:first-child {
width: 60%;
}
102 changes: 65 additions & 37 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type Config = {
speedPx?: number;
sizePx?: number;
clap?: string;
plant?: boolean;
};

function App() {
Expand All @@ -33,6 +34,9 @@ function App() {
const handleClapChange = (event: ChangeEvent<HTMLSelectElement>) => {
setConfig((prev) => ({ ...prev, clap: event.target.value }));
};
const handlePlantChange = (event: ChangeEvent<HTMLInputElement>) => {
setConfig((prev) => ({ ...prev, plant: event.target.checked }));
};

const handleStart = async () => {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
Expand Down Expand Up @@ -98,44 +102,68 @@ function App() {
return (
<div className="App">
<header className="App-header">
<h1>GoogleSlide Comment Stream</h1>
<p>スライド側とユーザーツール側両方で「Start」をクリックしてください</p>
<h2>GoogleSlide Comment Stream</h2>
<p>Click "Start" on both the slide side and the presenter user tools side</p>
<form>
<label htmlFor="color">Comment Color: </label>
<input id="color" type="color" onChange={handleColorChange} value={config?.color || '#000000'}></input>
<br />

<label htmlFor="font">Comment Font: </label>
<select value={config?.font} onChange={handleFontChange}>
{fonts.map((font) => {
return (
<option key={font} value={font}>
{font}
</option>
);
})}
</select>
<br />

<label htmlFor="speed">Speed(px/frame): </label>
<input id="speed" type="number" onChange={handleSpeedPxChange} value={config?.speedPx || 5}></input>
<br />

<label htmlFor="size">Size(px): </label>
<input id="size" type="number" onChange={handleSizePxChange} value={config?.sizePx || 50}></input>
<br />

<label htmlFor="clap">Clap(color): </label>
<select value={config?.clap} onChange={handleClapChange}>
{claps.map((value) => {
return (
<option key={value} value={value}>
{value}
</option>
);
})}
</select>
<br />
<div className="pseudo-table">
<div className="pseudo-row">
<label htmlFor="color" className="pseudo-cell">Comment Color: </label>
<div className="pseudo-cell">
<input id="color" type="color" onChange={handleColorChange} value={config?.color || '#000000'}></input>
</div>
</div>

<div className="pseudo-row">
<label htmlFor="font" className="pseudo-cell">Comment Font: </label>
<div className="pseudo-cell">
<select value={config?.font} onChange={handleFontChange}>
{fonts.map((font) => {
return (
<option key={font} value={font}>
{font}
</option>
);
})}
</select>
</div>
</div>

<div className="pseudo-row">
<label htmlFor="speed" className="pseudo-cell">Speed(px/frame): </label>
<div className="pseudo-cell">
<input id="speed" type="number" onChange={handleSpeedPxChange} value={config?.speedPx || 5}></input>
</div>
</div>

<div className="pseudo-row">
<label htmlFor="size" className="pseudo-cell">Size(px): </label>
<div className="pseudo-cell">
<input id="size" type="number" onChange={handleSizePxChange} value={config?.sizePx || 50}></input>
</div>
</div>

<div className="pseudo-row">
<label htmlFor="clap" className="pseudo-cell">Clap(color): </label>
<div className="pseudo-cell">
<select value={config?.clap} onChange={handleClapChange}>
{claps.map((value) => {
return (
<option key={value} value={value}>
{value}
</option>
);
})}
</select>
</div>
</div>

<div className="pseudo-row">
<label htmlFor="plant" className="pseudo-cell">Use Plant(require option configuration): </label>
<div className="pseudo-cell">
<input id="plant" type="checkbox" onChange={handlePlantChange} checked={config?.plant}></input>
</div>
</div>
</div>
</form>

<div className="preview">
Expand Down

0 comments on commit 4aa2c8e

Please sign in to comment.