Skip to content

Commit

Permalink
Added chrome extension codebase for meeting notes
Browse files Browse the repository at this point in the history
  • Loading branch information
prasanthLalapeta committed Jul 17, 2020
0 parents commit 378c7f8
Show file tree
Hide file tree
Showing 18 changed files with 396 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
.DS_Store
build/
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Fidisys Meeting Notes - Chrome extension

- Capture video meetings as a text file without opening new tab or app.

### Extension Features:
- Taking notes during your online meetings when using google meet, Zoom etc.,
- Use markdown format while taking notes
- Drag notes extension anywhere in the meeting page.
- Export your meeting notes as a .txt file

## Available Scripts

In the project directory, you can run:

### `npm install`

Install necessary packages to run project

### `npm start`

Runs the app in the development mode.<br>
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.

## Testing Inside Chrome

To load as a developer extension inside of Chrome:

1. `npm run build` <br>
2. Navigate to `chrome://extensions/` in your browser <br>
3. Toggle the `Developer mode` switch on in the top right hand corner <br>
4. Click the `Load unpacked` button in the top left corner <br>
5. Select the `build` folder inside of this project folder <br>

Builds the app for Chrome to the `build` folder.<br>

See the magic and have a fun with chrome extension

<p align="center">😊Pull requests accepted with ❤️ - Team Fidisys</p>
42 changes: 42 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "fidisys-meeting-notes-extension",
"version": "1.0.0",
"private": true,
"dependencies": {
"html-to-text": "^5.1.1",
"jquery": "3.4.1",
"react": "^16.8.5",
"react-dom": "^16.8.5",
"react-draggable": "4.0.3",
"react-quill": "^1.3.5",
"react-scripts": "3.1.2"
},
"scripts": {
"start": "react-scripts start",
"build": "cross-env INLINE_RUNTIME_CHUNK=false react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app",
"env": {
"browser": true,
"webextensions": true
}
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"cross-env": "7.0.2"
}
}
5 changes: 5 additions & 0 deletions public/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* global chrome */

chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.sendMessage(tab.id, { message: 'load' });
});
38 changes: 38 additions & 0 deletions public/content.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* global chrome */

chrome.runtime.onMessage.addListener(function(request, sender, callback) {
main();
});

function main() {
// eslint-disable-next-line no-undef
const extensionOrigin = 'chrome-extension://' + chrome.runtime.id;
// eslint-disable-next-line no-restricted-globals
if (!location.ancestorOrigins.contains(extensionOrigin)) {
// Fetch the local React index.html page
// eslint-disable-next-line no-undef
fetch(chrome.runtime.getURL('index.html') /*, options */)
.then((response) => response.text())
.then((html) => {
const styleStashHTML = html.replace(/\/static\//g, `${extensionOrigin}/static/`);
// eslint-disable-next-line no-undef
$(styleStashHTML).appendTo('body');
})
.catch((error) => {
console.warn(error);
});
}
}

window.addEventListener("message", function(event) {
if (event.source !== window) return;
onDidReceiveMessage(event);
});

async function onDidReceiveMessage(event) {
if (event.data.type && (event.data.type === "GET_EXTENSION_ID")) {
window.postMessage({ type: "EXTENSION_ID_RESULT", extensionId: chrome.runtime.id }, "*");
}
}


Binary file added public/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/icon128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/icon16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/icon48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div id="modal-window"></div>
2 changes: 2 additions & 0 deletions public/jquery.js

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"manifest_version": 2,
"name": "Fidisys Meeting Notes",
"description": "Capture video meetings as a text file without opening new tab or app",
"version": "1.0.0",
"icons": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
},
"browser_action": {
"default_icon": "favicon.png",
"default_title": "Fidisys Meeting Notes"
},
"background": {
"scripts": ["./jquery.js", "background.js"]
},
"content_scripts": [{
"matches": ["<all_urls>"],
"all_frames": true,
"js": [
"./content.js",
"./jquery.js"
],
"run_at": "document_end"
}],
"permissions": [
"activeTab"
],
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"web_accessible_resources": [
"index.html",
"/static/*"
]
}
83 changes: 83 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');

#modal-window {
position: absolute;
width: 99%;
top: 0px;
right: 0px;
z-index: 99999;
font-family: 'Inter', sans-serif;
}

.modal-window {
width: 350px;
min-height: 400px;
position: absolute;
top: 0;
right: 0;
will-change: transform;
background: #F9F9F9;
border-radius: 3px;
box-shadow: 0px 8px 16px rgba(0,0,0,0.25);
z-index: 9999;
}

.modal-handle {
padding: 15px;
cursor: move;
background-color: #FFFFFF;
border-bottom: 1px solid #ddd;
display: flex;
align-items: center;
justify-content: space-between;
}

.meeting-title {
font-size: 16px;
margin: 0px;
color: rgba(0,0,0,.87);
}

.download-btn {
width: 30px;
height: 30px;
border-radius: 100%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}

.download-btn:hover {
opacity: .87;
background-color: rgba(95,99,104,0.157);
}

.download-btn svg {
width: 20px;
height: 20px;
fill: #666666;
}

.modal-content-drawer-handle {
height: 100%;
width: 20px;
position: absolute;
display: flex;
justify-content: center;
align-items: center;
}

.modal-body {
display: flex;
flex-direction: column;
}

.ql-editor.ql-blank::before {
font-style: normal !important;
}

.ql-container {
font-size: 14px !important;
}

17 changes: 17 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import './App.css';
import Modal from './Components/Modal';
import ModalProvider from './Contexts/ModalProvider';

/**
* @return {null}
*/
function App() {
return (
<ModalProvider>
<Modal />
</ModalProvider>
);
}

export default App;
58 changes: 58 additions & 0 deletions src/Components/Modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React, { useState } from "react";
import Draggable from "react-draggable";
import ReactQuill from "react-quill";
import "react-quill/dist/quill.bubble.css";
import { ModalContext } from "../Contexts/ModalProvider";

const Modal = () => {
const [value, setValue] = useState("");
return (
<ModalContext.Consumer>
{({ windowPosition, hasDraggedWindowPosition, saveNotes }) => (
<Draggable
handle=".modal-handle"
defaultPosition={{ x: windowPosition.x, y: windowPosition.y }}
position={
hasDraggedWindowPosition
? { x: windowPosition.x, y: windowPosition.y }
: null
}
>
<div
id="modal"
className="modal-window"
style={{
transform: windowPosition
}}
>
<div className="modal-handle">
<h1 className="meeting-title">Meeting Notes</h1>
<div className="download-btn" onClick={saveNotes}>
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<title>Download Meeting Notes</title>
<path d="M27.414 19.414l-10 10c-0.781 0.781-2.047 0.781-2.828 0l-10-10c-0.781-0.781-0.781-2.047 0-2.828s2.047-0.781 2.828 0l6.586 6.586v-19.172c0-1.105 0.895-2 2-2s2 0.895 2 2v19.172l6.586-6.586c0.39-0.39 0.902-0.586 1.414-0.586s1.024 0.195 1.414 0.586c0.781 0.781 0.781 2.047 0 2.828z"></path>
</svg>
</div>
</div>
<div className="modal-body" id="meeting-notes">
<ReactQuill
theme="bubble"
value={value}
onChange={setValue}
placeholder="Click here and take a note..."
/>
</div>
</div>
</Draggable>
)}
</ModalContext.Consumer>
);
};

export default Modal;
51 changes: 51 additions & 0 deletions src/Contexts/ModalProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from "react";
import htmlToText from "html-to-text";
import useWindowPosition from "../Hooks/useWindowPosition";

export const ModalContext = React.createContext({});

const ModalProvider = ({ children }) => {
const { windowPosition } = useWindowPosition();

function saveNotes() {
const domNode = document.querySelector("#meeting-notes");
const toTextData = new Blob([htmlToText.fromString(domNode.innerHTML)], {
type: "text/plain"
});
const blobUrl = window.URL.createObjectURL(toTextData);
// Create a link element
const link = document.createElement("a");
// Set link's href to point to the Blob URL
link.href = blobUrl;
link.download = 'meetingNotes.txt';

// Append link to the body
document.body.appendChild(link);

// Dispatch click event on the link
// This is necessary as link.click() does not work on the latest firefox
link.dispatchEvent(
new MouseEvent("click", {
bubbles: true,
cancelable: true,
view: window
})
);

// Remove link from body
document.body.removeChild(link);
}

return (
<ModalContext.Provider
value={{
saveNotes,
windowPosition
}}
>
{children}
</ModalContext.Provider>
);
};

export default ModalProvider;
8 changes: 8 additions & 0 deletions src/Hooks/useWindowPosition.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

const useWindowPosition = () => {
return {
windowPosition: { x: 0, y: 0 },
}
};

export default useWindowPosition;
Loading

0 comments on commit 378c7f8

Please sign in to comment.