Skip to content

Commit

Permalink
Add more scrolls option (#151)
Browse files Browse the repository at this point in the history
* 🚚  Move files to separate folders

* 🔧  Added rollup setup config

* ✨ Setup rollup for popup

* ♻️ Migrated existing popup functionality to svelte

* ⬆️  Upgrade to v3 manifest and update according to the new APIs

* ✨ Udpated the CRUD methods

* ♻️ Moved content scripts to separte file

* ✨ Fix background script

* ♻️ Simplify get scroll logic

* ♻️ Updated options file for new changes

* 📝  Updated readme
  • Loading branch information
prateek3255 authored May 19, 2021
1 parent a8b345c commit 8cd9b9e
Show file tree
Hide file tree
Showing 35 changed files with 3,135 additions and 5,760 deletions.
28 changes: 28 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
webextensions: true,
},
extends: ['airbnb-base', 'eslint:recommended', 'prettier'],
parserOptions: {
ecmaVersion: 12,
sourceType: 'module',
},
rules: {
'prettier/prettier': [
'error',
{
arrowParens: 'avoid',
printWidth: 120,
singleQuote: true,
tabWidth: 2,
trailingComma: 'es5',
endOfLine: 'auto',
},
],
'no-prototype-builtins': 0,
},
plugins: ['prettier'],
};
5 changes: 3 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"eslint.autoFixOnSave": true,
"editor.formatOnSave": true
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ I know there are a few extensions that already serve this purpose, but most of t

Under the hood, this extension uses the [chrome localStorage API](https://developer.mozilla.org/en/DOM/Storage#localStorage) to store the scroll positions for different webpages. I avoided using sync storage due to its storage limitations ([read more](https://developer.chrome.com/apps/storage)). This extension creates an object which stores the URL as keys and the scroll position as values.

The functions for adding or updating, reading and deleting are in the files `save.js`, `get.js` and `delete.js` respectively, which are executed as content scripts from `popup.js` whenever the respective button is clicked.
The functions for adding or updating, reading and deleting are in the files `save.js`, `get.js`, `update.js` and `delete.js` respectively, which are executed as content scripts from popup whenever the respective button is clicked.

The `background.js` handles switching icon color whenever a tab is changed, or the URL is updated.

The popup sheet is also handled by `popup.js` by dynamically changing the UI following the availability of the URL in the localStorage object.
The popup sheet is a Svelte app in the `popup` folder that controls the UI and the behaviour of the extension popup.

## Features 🚀

- You can **save** the scroll position of the page and then revisit the page at any time to continue where you left.
- Also, extension allows you to save **multiple scrolls** from the page.
- The extension also allows you to save **multiple scrolls** for the page.
- You can **fetch** the last saved scroll.
- If you dont want a scroll you can **delete** it.
- Extension allows you to see all the saved scrolls arranged in the newly first order where you can **delete** any specific scroll or **clear all** the scrolls at once.
Expand All @@ -48,12 +48,14 @@ The popup sheet is also handled by `popup.js` by dynamically changing the UI fol

## Development 💻

To run the extension locally follow these steps:
The JavaScript files in the extension are compiled using [rollup](https://rollupjs.org), and as of now the extension popup is written with Svelete which is also compiled via rollup. To run the extension locally follow these steps:

- Run `yarn` or `npm install` to install the dependencies.
- Then run `yarn dev` or `npm run dev` to build the extension in watch mode, you'll see a build folder created with all the necessary files for the extension to run.
- Visit `chrome://extensions` and turn on developer mode.
- Click on `Load unpacked` at the top left and select the extension root folder.
- Now you can go ahead and modify `popup.js` or `popup.html`. Changes would directly be visible in the extension.
- If you change something in `background.js` or `manifest.json` then you will need to reload the extension.
- Click on `Load unpacked` at the top left and select the `build` folder that was created with the `dev` command.
- Now you can go ahead and modify the js files and the Svelte app, the dev command would automatically build the updated files accordingly. You would be able to view the changes in the extension directly.
- If you change something in `background.js` you'll need to reload the extension for the changes to appear. For other files like `manifest.json` you would need to restart the dev server as well so that a fresh copy can be created.

## Contributing 🌏

Expand Down
Binary file removed Roboto/Roboto-Bold.ttf
Binary file not shown.
1 change: 0 additions & 1 deletion background.html

This file was deleted.

144 changes: 72 additions & 72 deletions background.js
Original file line number Diff line number Diff line change
@@ -1,112 +1,112 @@
import {
executeSaveScroll,
executeGetScroll,
executeDeleteScroll,
} from "./helpers.js";
import { executeSaveScroll, executeGetScroll } from './contentScripts';

function getUrlWithoutHash(url) {
return url.split("?")[0];
return url.split('?')[0];
}

chrome.runtime.setUninstallURL(
"https://prateeksurana3255.typeform.com/to/VMfEV6"
);
const setActiveIcon = () => {
chrome.action.setIcon({
path: {
16: '../images/icon-16.png',
32: '../images/icon-32.png',
48: '../images/icon-48.png',
128: '../images/icon-128.png',
256: '../images/icon-256.png',
},
});
};

const setInactiveIcon = () => {
chrome.action.setIcon({
path: {
16: '../images/icon-16-inactive.png',
32: '../images/icon-32-inactive.png',
48: '../images/icon-48-inactive.png',
128: '../images/icon-128-inactive.png',
256: '../images/icon-256-inactive.png',
},
});
};

chrome.runtime.onInstalled.addListener((details) => {
if (details.reason === "install") {
chrome.storage.local.set({ "scroll-mark": {} });
chrome.runtime.setUninstallURL('https://prateeksurana3255.typeform.com/to/VMfEV6');

chrome.runtime.onInstalled.addListener(details => {
if (details.reason === 'install') {
chrome.storage.local.set({ 'scroll-mark': {} });
}
});

const updateIcon = () => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
const url = getUrlWithoutHash(tabs[0].url);
// const updateIcon = () => {
// chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
// const url = getUrlWithoutHash(tabs[0].url);

chrome.storage.local.get("scroll-mark", (data) => {
const scrollMarkData = data["scroll-mark"];
if (!scrollMarkData.hasOwnProperty(url)) {
setInactiveIcon();
} else {
setActiveIcon();
}
});
});
};
// chrome.storage.local.get('scroll-mark', data => {
// const scrollMarkData = data['scroll-mark'];
// if (!scrollMarkData.hasOwnProperty(url)) {
// setInactiveIcon();
// } else {
// setActiveIcon();
// }
// });
// });
// };

chrome.tabs.onActivated.addListener(() => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
const url = getUrlWithoutHash(tabs[0].url);

chrome.storage.local.get("scroll-mark", (data) => {
const scrollMarkData = data["scroll-mark"];
if (!scrollMarkData.hasOwnProperty(url)) {
setInactiveIcon();
} else {
chrome.storage.local.get('scroll-mark', data => {
const scrollMarkData = data['scroll-mark'];
if (scrollMarkData && scrollMarkData[url] !== undefined) {
setActiveIcon();
} else {
setInactiveIcon();
}
});
});
});

chrome.tabs.onUpdated.addListener((tabId, updateObj) => {
chrome.tabs.get(tabId, (tab) => {
chrome.tabs.onUpdated.addListener(tabId => {
chrome.tabs.get(tabId, tab => {
const url = getUrlWithoutHash(tab.url);

if (url) {
chrome.storage.local.get("scroll-mark", (data) => {
const scrollMarkData = data["scroll-mark"];
if (!scrollMarkData.hasOwnProperty(url)) {
setInactiveIcon();
} else {
executeGetScroll(tabId);
chrome.storage.local.get('scroll-mark', data => {
const scrollMarkData = data['scroll-mark'];
if (scrollMarkData && scrollMarkData[url] !== undefined) {
executeGetScroll(tabId, null, true);
setActiveIcon();
} else {
setInactiveIcon();
}
});
}
});
});

chrome.runtime.onMessage.addListener((request, sender) => {
if (request === "setActive") {
chrome.runtime.onMessage.addListener(request => {
if (request === 'setActive') {
setActiveIcon();
} else {
setInactiveIcon();
}
});

chrome.commands.onCommand.addListener(function (command) {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
chrome.commands.onCommand.addListener(command => {
chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
const currentTabId = tabs[0].id;
if (command === "save-or-update-scroll") {
if (command === 'save-scroll') {
executeSaveScroll(currentTabId);
} else if (command === "delete-scroll") {
executeDeleteScroll(currentTabId);
} else if (command === "fetch-scroll") {
executeGetScroll(currentTabId);
} else if (command === 'fetch-scroll') {
chrome.tabs.get(currentTabId, tab => {
const url = getUrlWithoutHash(tab.url);
chrome.storage.local.get('scroll-mark', data => {
const scrollMarkData = data['scroll-mark'];
if (scrollMarkData && scrollMarkData[url] !== undefined) {
executeGetScroll(currentTabId, null, true);
}
});
});
}
});
});

const setActiveIcon = () => {
chrome.browserAction.setIcon({
path: {
"16": "images/icon-16.png",
"32": "images/icon-32.png",
"48": "images/icon-48.png",
"128": "images/icon-128.png",
"256": "images/icon-256.png",
},
});
};

const setInactiveIcon = () => {
chrome.browserAction.setIcon({
path: {
"16": "images/icon-16-inactive.png",
"32": "images/icon-32-inactive.png",
"48": "images/icon-48-inactive.png",
"128": "images/icon-128-inactive.png",
"256": "images/icon-256-inactive.png",
},
});
};
33 changes: 33 additions & 0 deletions contentScripts/delete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { getURL, getItemFromStorage } from './index';

(async function () {
const url = getURL();

const data = await getItemFromStorage('scroll-mark');
const scrollMarkData = data['scroll-mark'];
const urlData = scrollMarkData[url];

let updatedURLData;

const shouldClearAll = (await getItemFromStorage('clear-all'))['clear-all'];

if (Array.isArray(urlData) && !shouldClearAll) {
const currentScrollData = await getItemFromStorage('current-scroll-id');
const id = currentScrollData['current-scroll-id'];

updatedURLData = urlData.filter(item => item.uuid !== id);

if (updatedURLData.length === 0) {
updatedURLData = undefined;
}
}

if (scrollMarkData[url]) {
const updatedData = { ...scrollMarkData, [url]: updatedURLData };
chrome.storage.local.set({ 'scroll-mark': updatedData }, () => {
if (updatedURLData === undefined) {
chrome.runtime.sendMessage('setInactive');
}
});
}
})();
35 changes: 35 additions & 0 deletions contentScripts/get.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { getURL, getItemFromStorage } from './index';

(async function () {
const url = getURL();
const data = await getItemFromStorage('scroll-mark');

const scrollMarkData = data['scroll-mark'];
let offset = null;
const urlData = scrollMarkData[url];

if (typeof urlData.offset === 'number') {
offset = urlData.offset;
} else if (Array.isArray(urlData)) {
const latestScroll = await getItemFromStorage('fetch-latest-item');
const shouldFetchLatestItem = latestScroll['fetch-latest-item'];

let item = null;

if (shouldFetchLatestItem) {
item = urlData.reduce((prev, current) => (prev.offset > current.offset ? prev : current), { offset: 0 });
} else {
const currentScrollData = await getItemFromStorage('current-scroll-id');
const id = currentScrollData['current-scroll-id'];
item = urlData.find(currentItem => currentItem.uuid === id);
}

if (item) {
offset = item.offset;
}
}

if (offset) {
window.scrollTo({ left: 0, top: offset, behavior: 'smooth' });
}
})();
39 changes: 39 additions & 0 deletions contentScripts/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export const executeScript = (tabId, file) => {
chrome.scripting.executeScript({
target: { tabId },
files: [file],
});
};

export const MAX_SCROLLS = 20;

export const getURL = () => {
const fullUrl = window.location.href;
return fullUrl.split('?')[0];
};

export const getItemFromStorage = name =>
new Promise(resolve => {
chrome.storage.local.get(name, data => {
resolve(data);
});
});

export const executeSaveScroll = tabId => {
executeScript(tabId, 'contentScripts/save.js');
};

export const executeDeleteScroll = (tabId, scrollId, clearAll = false) => {
chrome.storage.local.set({ 'current-scroll-id': scrollId, 'clear-all': clearAll });
executeScript(tabId, 'contentScripts/delete.js');
};

export const executeGetScroll = (tabId, scrollId, fetchLatestItem = false) => {
chrome.storage.local.set({ 'current-scroll-id': scrollId, 'fetch-latest-item': fetchLatestItem });
executeScript(tabId, 'contentScripts/get.js');
};

export const executeUpdateScroll = (tabId, scrollId) => {
chrome.storage.local.set({ 'current-scroll-id': scrollId });
executeScript(tabId, 'contentScripts/update.js');
};
Loading

0 comments on commit 8cd9b9e

Please sign in to comment.