Skip to content

Commit

Permalink
🔀 Sidebar fixes and cleanup (#4)
Browse files Browse the repository at this point in the history
Sidebar Fixes and Cleanup
  • Loading branch information
trickypr authored Dec 24, 2022
2 parents eecd299 + c8862a6 commit 8c36610
Show file tree
Hide file tree
Showing 11 changed files with 309 additions and 141 deletions.
4 changes: 4 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"semi": false,
"singleQuote": true
}
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# sidebar-tabs
An add-on for Pulse Browser that lets you save pages directly to your sidebar. This add-on was developed solely for Pulse Browser and makes use of its specific APIs.

#### Sidebar View Icon
Sidebar View Icon from Side-View [\[MPL-2.0\]](https://github.com/mozilla/side-view/blob/master/LICENSE) - Available at https://github.com/mozilla/side-view/blob/master/addon/images/in-content-icon.png
9 changes: 9 additions & 0 deletions background.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
</head>
<body>
<script type="module" src="js/background.js"></script>
</body>
</html>
Binary file added images/in-content-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 8 additions & 5 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles/main.css">
<meta charset="utf-8" />
<link rel="stylesheet" href="styles/main.css" />
</head>
<body>
<p id="instructions">Add websites/items to your sidebar for ease of access.</p>
<input type="text" id="url" placeholder="Enter URL">
<img src="images/in-content-icon.png" width="200" />
<p id="instructions">
Add websites/items to your sidebar for ease of access.
</p>
<input type="text" id="url" placeholder="Enter URL" />
<button id="addPageButton">Add Page</button>
<script src="js/main.js"></script>
<script type="module" src="js/main.js"></script>
</body>
</html>
145 changes: 111 additions & 34 deletions js/background.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,124 @@
browser.sidebars.add({
title: "Add Shortcut to Sidebar",
iconUrl: "plus.svg",
webviewUrl: "index.html",
isBottom: true,
})
import { MessageTypeValues } from './message.js'
import { Mutex } from './mutex.js'

// =============================================================================
// Types
// @ts-check

/**
* @typedef {object} SidebarItem
*
* @property {number} id The id of this specific sidebar item. Valid for a browser session
* @property {string} title The title of the sidebar item, generally the webpage title
* @property {string} iconUrl The website's favicon
* @property {string} webviewUrl The url of the website to navigate to
*/

/**
* This is an incomplete version of {@link SidebarItem} intended for sending
* creation data around with. Currently it only excludes `id`.
*
* @typedef {object} SidebarItemData
*
* @property {string} title The title of the sidebar item, generally the webpage title
* @property {string} iconUrl The website's favicon
* @property {string} webviewUrl The url of the website to navigate to
*/

// =============================================================================
// Functions

getFromStorage().then(createSidebarItems);
const storageMutex = new Mutex()

/**
* @returns {Promise<{ sidebaritems: SidebarItem[] }>}
*
* @note We assume that the parent caller has already locked {@see storageMutex} to avoid a race condition
*/
async function getFromStorage() {
//get from storage
let sidebaritems = await browser.storage.local.get("sidebaritems");
console.log(sidebaritems);
return sidebaritems;
const storage = await browser.storage.local.get('sidebaritems')
return storage
}

async function createSidebarItems(sidebaritems) {
//create sidebar items
for (let i = 0; i < sidebaritems.sidebaritems.length; i++) {
browser.sidebars.add({
title: sidebaritems.sidebaritems[i].title,
iconUrl: sidebaritems.sidebaritems[i].iconUrl,
webviewUrl: sidebaritems.sidebaritems[i].webviewUrl,
});
}
async function spawnExistingSidebarItems() {
const { unlock } = await storageMutex.lock()
let storage = await getFromStorage()
let { sidebaritems: sidebarItems } = storage

if (typeof sidebarItems === 'undefined') {
sidebarItems = []
}

for (let i = 0; i < sidebarItems.length; i++) {
const item = sidebarItems[i]
const id = await browser.sidebars.add(item)

sidebarItems[i].id = id
}

storage.sidebaritems = sidebarItems
await browser.storage.local.set(storage)
unlock()
}

async function removeSidebarItems(itemId)
{
const item = await browser.sidebars.get(itemId);
/**
* @param {number} idToRemove The id of the sidebar item you wish to remove
*/
async function removeSidebarItems(idToRemove) {
const { unlock } = await storageMutex.lock()
let storage = await getFromStorage()

storage.sidebaritems = storage.sidebaritems.filter(
(item) => item.id !== idToRemove
)

//remove from storage
let storagearray = await getFromStorage();
let storageitems = storagearray.sidebaritems;
await browser.storage.local.set(storage)
unlock()
}

/**
* Creates a sidebar item and registers it, storing it for future browser
* instances
*
* @param {SidebarItemData} itemInfo Information about the sidebar to be created
*/
async function createSidebarItem(itemInfo) {
const { unlock } = await storageMutex.lock()
const storage = await getFromStorage()

for (let i = 0; i < storageitems.length; i++) {
if (storageitems[i].webviewUrl == item.webviewUrl) {
storageitems.splice(i, 1);
}
}
const id = await browser.sidebars.add(itemInfo)
storage.sidebaritems.push({ ...itemInfo, id })

browser.storage.local.set(storagearray);
await browser.storage.local.set(storage)
unlock()
}

browser.sidebars.onRemove.addListener((itemId) => {
removeSidebarItems(itemId);
// =============================================================================
// Init logic

browser.sidebars.add({
title: 'Add Shortcut to Sidebar',
iconUrl: 'plus.svg',
webviewUrl: 'index.html',
isBottom: true,
})

spawnExistingSidebarItems()

browser.sidebars.onRemove.addListener((itemId) => removeSidebarItems(itemId))

// =============================================================================
// Messages from UI

browser.runtime.onMessage.addListener(({ type, data }, sender) => {
switch (type) {
case MessageTypeValues.CREATE_SIDEBAR_ITEMS:
createSidebarItem(data)
break

default:
throw new Error(`Unknown message type: ${type}`)
}

return null
})
154 changes: 91 additions & 63 deletions js/main.js
Original file line number Diff line number Diff line change
@@ -1,77 +1,105 @@
// @ts-check
import { MessageTypeValues } from './message.js'

/**
* Send a message to the background page to be processed
* @param {import("./message").MessageType} type
* @param {*} data
*/
async function sendMessageToBackgroundScript(type, data) {
return await browser.runtime.sendMessage({ type, data })
}

async function createSidebarItem(title, iconUrl, webviewUrl) {
await sendMessageToBackgroundScript(MessageTypeValues.CREATE_SIDEBAR_ITEMS, {
title,
iconUrl,
webviewUrl,
})
}

async function addPage() {
var pageURL = (document.getElementById("url").value);
if (!pageURL) {
return;
/** @type {HTMLInputElement?} */
// @ts-ignore
const pageUrlElement = document.getElementById('url')

if (!pageUrlElement) {
throw new Error('Could not find the URL element')
}

// remove https:// or http:// from the url
pageURL = pageURL.replace(/^https?:\/\//, '');
const pageUrl = pageUrlElement.value.replace(/^https?:\/\//, '')
let pageIcon = 'chrome://global/skin/icons/link.svg'

let pageIcon = "chrome://global/skin/icons/link.svg";
if (!navigator.onLine) {
createSidebarItem(pageUrl, pageIcon, 'https://' + pageUrl)
return
}

if (navigator.onLine) {
//fetch https://icons.duckduckgo.com/ip3 image for icon
var response = await fetch(`https://icons.duckduckgo.com/ip3/${pageURL}.ico`);
if (response.ok) {
//Update pageIcon with the fetched icon
pageIcon = ('https://icons.duckduckgo.com/ip3/'+pageURL+'.ico')
}
const response = await fetch(
`https://icons.duckduckgo.com/ip3/${pageUrl}.ico`
)
if (response.ok) {
pageIcon = `https://icons.duckduckgo.com/ip3/${pageUrl}.ico`
}

if (navigator.onLine) {
//create new tab
let newtab = browser.tabs.create({ url: "https://"+pageURL, active: true});
//newtab await complete load
newtab.then(function(tab) {
browser.tabs.onUpdated.addListener(function listener(tabId, changeInfo, tab) {
if (tabId == tab.id && changeInfo.status == "complete") {
//remove listener
browser.tabs.onUpdated.removeListener(listener);
browser.tabs.get(tab.id).then(function(tab) {
//create new sidebar item
browser.sidebars.add({
title: tab.title,
iconUrl: pageIcon,
webviewUrl: tab.url,
});

//save to storage
saveToStorage({
title: tab.title,
iconUrl: pageIcon,
webviewUrl: tab.url,
});
});
}
});
});

const tab = await browser.tabs.create({
url: 'https://' + pageUrl,
active: true,
})

browser.tabs.onUpdated.addListener(async function listener(
tabId,
changeInfo,
tab
) {
if (tabId == tab.id && changeInfo.status == 'complete') {
browser.tabs.onUpdated.removeListener(listener)
createSidebarItem(tab.title, pageIcon, tab.url)
}
})
}

document.getElementById('addPageButton').addEventListener('click', addPage)

//Stolen from https://github.com/mdn/webextensions-examples
function setSidebarStyle(theme) {
const body = document.body

if (theme.colors && theme.colors.frame) {
body.style.backgroundColor = theme.colors.frame
} else {
body.style.backgroundColor = 'white'
}
else
{
//If the user is offline, create a sidebar item with the title as the url
//and the icon as the default icon so the browser sidebar is functional offline
browser.sidebars.add({
title: pageURL,
iconUrl: pageIcon,
webviewUrl: pageURL,
});

if (theme.colors && theme.colors.toolbar) {
body.style.backgroundColor = theme.colors.toolbar
} else {
body.style.backgroundColor = '#ebebeb'
}

if (theme.colors && theme.colors.toolbar_text) {
body.style.color = theme.colors.toolbar_text
} else {
body.style.color = 'black'
}
}

async function saveToStorage({title: title, iconUrl: iconUrl, webviewUrl: webviewUrl}) {
//save to sidebaritems
let sidebaritems = await browser.storage.local.get("sidebaritems");
if (sidebaritems.sidebaritems == undefined) {
sidebaritems.sidebaritems = [];
}
sidebaritems.sidebaritems.push({
title: title,
iconUrl: iconUrl,
webviewUrl: webviewUrl,
});
browser.storage.local.set(sidebaritems);
// Set the element style when the extension page loads
async function setInitialStyle() {
const theme = await browser.theme.getCurrent()
setSidebarStyle(theme)
}
setInitialStyle()

document.getElementById("addPageButton").addEventListener("click", addPage);
// Watch for theme updates
browser.theme.onUpdated.addListener(async ({ theme, windowId }) => {
const sidebarWindow = await browser.windows.getCurrent()
/*
Only update theme if it applies to the window the sidebar is in.
If a windowId is passed during an update, it means that the theme is applied to that specific window.
Otherwise, the theme is applied globally to all windows.
*/
if (!windowId || windowId == sidebarWindow.id) {
setSidebarStyle(theme)
}
})
11 changes: 11 additions & 0 deletions js/message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @typedef {string} MessageType
*/

/**
* @readonly
* @enum {MessageType}
*/
export const MessageTypeValues = {
CREATE_SIDEBAR_ITEMS: 'createSidebarItems',
}
Loading

0 comments on commit 8c36610

Please sign in to comment.