Skip to content

Commit

Permalink
Add cookie-based cache with expiration (#1529)
Browse files Browse the repository at this point in the history
  • Loading branch information
fregante authored and sindresorhus committed Sep 19, 2018
1 parent 0749bb7 commit 0a1ee62
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 52 deletions.
13 changes: 9 additions & 4 deletions source/background.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import OptionsSync from 'webext-options-sync';
import domainPermissionToggle from 'webext-domain-permission-toggle';
import dynamicContentScripts from 'webext-dynamic-content-scripts';
import './libs/cache';

// Define defaults
new OptionsSync().define({
Expand Down Expand Up @@ -39,10 +40,6 @@ browser.runtime.onMessage.addListener(async message => {
});

browser.runtime.onInstalled.addListener(async ({reason}) => {
// Cleanup old key
// TODO: remove in the future
browser.storage.local.remove('userWasNotified');

// Only notify on install
if (reason === 'install') {
const {installType} = await browser.management.getSelf();
Expand All @@ -54,6 +51,14 @@ browser.runtime.onInstalled.addListener(async ({reason}) => {
active: false
});
}

// Nuke old cache
// TODO: drop code in November
if (reason === 'update') {
const dataToPreserve = await browser.storage.local.get('unreadNotifications');
await browser.storage.local.clear();
browser.storage.local.set(dataToPreserve);
}
});

// GitHub Enterprise support
Expand Down
18 changes: 9 additions & 9 deletions source/features/add-branch-buttons.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {h} from 'dom-chef';
import select from 'select-dom';
import compareVersions from 'tiny-version-compare';
import * as icons from '../libs/icons';
import * as cache from '../libs/cache';
import {appendBefore} from '../libs/utils';
import {groupSiblings} from '../libs/group-buttons';
import {getRepoURL, isRepoRoot, getOwnerAndRepo} from '../libs/page-detect';
Expand Down Expand Up @@ -44,12 +45,12 @@ function getTagLink() {
return link;
}

function getDefaultBranchNameIfDifferent() {
async function getDefaultBranchNameIfDifferent() {
const {ownerName, repoName} = getOwnerAndRepo();
const cacheKey = `rgh-default-branch-${ownerName}-${repoName}`;
const cacheKey = `default-branch:${ownerName}/${repoName}`;

// Return the cached name if it differs from the current one
const cachedName = sessionStorage.getItem(cacheKey);
const cachedName = await cache.get(cacheKey);
if (cachedName) {
const currentBranch = select('[data-hotkey="w"] span').textContent;
return cachedName === currentBranch ? false : cachedName;
Expand All @@ -64,18 +65,17 @@ function getDefaultBranchNameIfDifferent() {
// Parse the infobar
const [, branchName] = branchInfo.textContent.trim().match(branchInfoRegex) || [];
if (branchName) {
// Temporarily cache it between loads to enable it on files
sessionStorage.setItem(cacheKey, branchName);
cache.set(cacheKey, branchName, 1);
return branchName;
}
}

function getDefaultBranchLink() {
async function getDefaultBranchLink() {
if (select.exists('.repohead h1 .octicon-repo-forked')) {
return; // It's a fork, no "default branch" info available #1132
}

const branchName = getDefaultBranchNameIfDifferent();
const branchName = await getDefaultBranchNameIfDifferent();
if (!branchName) {
return;
}
Expand Down Expand Up @@ -103,14 +103,14 @@ function getDefaultBranchLink() {
);
}

export default function () {
export default async function () {
const container = select('.file-navigation');
if (!container) {
return;
}
const wrapper = (
<div class="rgh-branch-buttons">
{getDefaultBranchLink() || ''}
{await getDefaultBranchLink() || ''}
{getTagLink() || ''}
</div>
);
Expand Down
12 changes: 6 additions & 6 deletions source/features/add-releases-tab.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,25 @@ The tab isn’t shown if there are no releases.
import {h} from 'dom-chef';
import select from 'select-dom';
import * as icons from '../libs/icons';
import * as cache from '../libs/cache';
import * as pageDetect from '../libs/page-detect';
import {registerShortcut} from './improve-shortcut-help';

const repoUrl = pageDetect.getRepoURL();
const repoKey = `releases-count-${repoUrl}`;
const repoKey = `releases-count:${repoUrl}`;

// Get as soon as possible, to have it ready before the first paint
let storageMap = browser.storage.local.get(repoKey);
const cached = cache.get(repoKey);

async function updateReleasesCount() {
function updateReleasesCount() {
if (pageDetect.isRepoRoot()) {
const releasesCountEl = select('.numbers-summary a[href$="/releases"] .num');
const releasesCount = Number(releasesCountEl ? releasesCountEl.textContent.replace(/,/g, '') : 0);
storageMap = {[repoKey]: releasesCount};
browser.storage.local.set(storageMap);
cache.set(repoKey, releasesCount, 3);
return releasesCount;
}

return (await storageMap)[repoKey];
return cached;
}

export default async () => {
Expand Down
63 changes: 30 additions & 33 deletions source/features/show-names.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import {h} from 'dom-chef';
import select from 'select-dom';
import domify from '../libs/domify';
import * as cache from '../libs/cache';
import {getUsername, groupBy} from '../libs/utils';

const storageKey = 'cachedNames';

const getCachedUsers = async () => {
const keys = await browser.storage.local.get({
[storageKey]: {}
});
return keys[storageKey];
};

const fetchName = async username => {
// /following/you_know is the lightest page we know
// location.origin is required for Firefox #490
Expand All @@ -29,40 +21,45 @@ const fetchName = async username => {
return fullname;
};

export default async () => {
export default () => {
const myUsername = getUsername();
const cache = await getCachedUsers();
const commentsList = select.all('.js-discussion .author:not(.rgh-fullname):not([href^="/apps/"])');

// {sindresorhus: [a.author, a.author], otheruser: [a.author]}
const selector = '.js-discussion .author:not(.refined-github-fullname):not([href^="/apps/"])';
const usersOnPage = groupBy(select.all(selector), el => el.textContent);
const usersOnPage = groupBy(commentsList, el => el.textContent);

// Drop 'commented' label to shorten the copy
for (const usernameEl of commentsList) {
const commentedNode = usernameEl.parentNode.nextSibling;
if (commentedNode && commentedNode.textContent.includes('commented')) {
commentedNode.remove();
}
}

const fetchAndAdd = async username => {
if (typeof cache[username] === 'undefined' && username !== myUsername) {
cache[username] = await fetchName(username);
if (username === myUsername) {
return;
}

for (const usernameEl of usersOnPage[username]) {
const commentedNode = usernameEl.parentNode.nextSibling;
if (commentedNode && commentedNode.textContent.includes('commented')) {
commentedNode.remove();
}
const cacheKey = `full-name:${username}`;
let fullname = await cache.get(cacheKey);
if (fullname === undefined) {
fullname = await fetchName(username);
cache.set(cacheKey, fullname);
}
if (!fullname) {
return;
}

usernameEl.classList.add('refined-github-fullname');
for (const usernameEl of usersOnPage[username]) {
usernameEl.classList.add('rgh-fullname');

if (cache[username] && username !== myUsername) {
// If it's a regular comment author, add it outside <strong>
// otherwise it's something like "User added some commits"
const insertionPoint = usernameEl.parentNode.tagName === 'STRONG' ? usernameEl.parentNode : usernameEl;
insertionPoint.after(' (', <bdo>{cache[username]}</bdo>, ') ');
}
// If it's a regular comment author, add it outside <strong>
// otherwise it's something like "User added some commits"
const insertionPoint = usernameEl.parentNode.tagName === 'STRONG' ? usernameEl.parentNode : usernameEl;
insertionPoint.after(' (', <bdo>{fullname}</bdo>, ') ');
}
};

const fetches = Object.keys(usersOnPage).map(fetchAndAdd);

// Wait for all the fetches to be done
await Promise.all(fetches);

browser.storage.local.set({[storageKey]: cache});
Object.keys(usersOnPage).map(fetchAndAdd);
};
57 changes: 57 additions & 0 deletions source/libs/cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
export default async function getSet(key, getter, expiration) {
const cache = await get(key);
if (cache === undefined) {
const value = getter();
if (value !== undefined) {
await set(key, value, expiration);
return value;
}
}
}

export async function get(key) {
const value = await browser.runtime.sendMessage({
key,
code: 'get-cache'
});

// If it's not in the cache, it's best to return "undefined"
if (value === null) {
return undefined;
}
return value;
}

export function set(key, value, expiration /* in days */) {
return browser.runtime.sendMessage({
key,
value,
expiration,
code: 'set-cache'
});
}

/* Accept messages in background page */
if (!browser.runtime.getBackground) {
browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (!request) {
return;
}
const {code, key, value, expiration} = request;
if (code === 'get-cache') {
const [cached] = document.cookie.split('; ')
.filter(item => item.startsWith(key + '='));

if (cached) {
const [, value] = cached.split('=');
sendResponse(JSON.parse(value));
} else {
sendResponse();
}
} else if (code === 'set-cache') {
// Store as JSON to preserve data type
// otherwise Booleans and Numbers become strings
document.cookie = `${key}=${JSON.stringify(value)}; max-age=${expiration ? expiration * 3600 * 24 : ''}`;
}
});
}

0 comments on commit 0a1ee62

Please sign in to comment.