Skip to content

Commit

Permalink
Merge pull request #238 from julianpoy/web-extension
Browse files Browse the repository at this point in the history
Web extension
  • Loading branch information
julianpoy authored Apr 12, 2019
2 parents c17da1d + 7de771a commit 4591931
Show file tree
Hide file tree
Showing 30 changed files with 1,019 additions and 50 deletions.
2 changes: 1 addition & 1 deletion Backend/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ var devMode = appConfig.environment === 'dev';

Raven.config(appConfig.sentry.dsn, {
environment: appConfig.environment,
release: '1.7.2'
release: '1.7.3'
}).install();

// Routes
Expand Down
50 changes: 26 additions & 24 deletions Backend/services/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,42 +107,44 @@ exports.formatS3ImageResponse = (key, mimetype, size, etag) => {
}
}

exports.convertImage = imageBuf => {
return new Promise((resolve, reject) => {
try {
sharp(imageBuf)
.rotate() // Rotates based on EXIF data
.resize(200, 200) // Uses object-fit: cover by default
.jpeg({
quality: 55,
// chromaSubsampling: '4:4:4'
})
.toBuffer((err, buffer, info) => {
if (err) reject(err);
resolve(buffer);
});
} catch (e) {
reject();
}
})
}

exports.sendURLToS3 = url => {
return exports.fetchImage(url).then(({ res, body }) => {

var contentType = res.headers['content-type'];
var contentLength = res.headers['content-length'];

return exports.sendBufferToS3(body).then(result => {
return exports.formatS3ImageResponse(result.key, contentType, contentLength, result.s3Response.ETag)
return exports.convertImage(body).then(convertedBuffer => {
return exports.sendBufferToS3(convertedBuffer).then(result => {
return exports.formatS3ImageResponse(result.key, "image/jpeg", Buffer.byteLength(convertedBuffer), result.s3Response.ETag);
});
});
})
}

exports.sendFileToS3 = path => {
return fs.readFile(path).then(buf => {
return new Promise((resolve, reject) => {
try {
sharp(buf)
.rotate() // Rotates based on EXIF data
.resize(200, 200) // Uses object-fit: cover by default
.jpeg({
quality: 55,
// chromaSubsampling: '4:4:4'
})
.toBuffer((err, buffer, info) => {
if (err) reject(err);
resolve(buffer);
});
} catch(e) {
reject()
}
})
return exports.convertImage(buf);
}).then(stream => {
return exports.sendBufferToS3(stream)
}).then(result => {
var stats = fs.statSync(path);
return exports.formatS3ImageResponse(result.key, 'image/png', stats["size"], result.s3Response.ETag)
return exports.formatS3ImageResponse(result.key, 'image/jpeg', stats["size"], result.s3Response.ETag)
})
}

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion Frontend/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
</script>

<script>
window.version = '1.7.2';
window.version = '1.7.3';
</script>

<link href="build/main.css" rel="stylesheet" async>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@

<br />

<b>v1.7.3</b><br />
- Added help page for new browser extension<br />
- Fixed an issue where image quality would be reduced in some browsers<br />
- Fixed an issue where selecting image for recipe would cause lag<br />
- Added a warning for images that are over the size limit<br />

<br />

<b>v1.7.2</b><br />
- Meal plan page now displays in side-by-side split view on wide screens<br />
- Fixed a bug where warning for old browsers (at or below IE11) would not trigger<br />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,63 @@ <h2>Printing a Recipe</h2>
</p>
</div>

<div *ngIf="active == 'webExtension'" padding>
<br />
<h2>Clip Tool Browser Extension Installation</h2>
<div class="tutorial-image">
<img src="assets/tutorial/webextension-on-rs.png" />
</div>
<p>
Adding the RecipeSage browser extension allows you to quickly select text from a webpage and save recipes directly to your
account via the handy clip tool. You can trigger the clip tool on most websites to grab text and import recipes
into your collection.<br /><br />
To install the RecipeSage browser extension, you'll need to be using a browser that supports WebExtensions.<br />
At the time of writing, this includes Firefox and Google Chrome.<br /><br />
If you're using Google Chrome, <a href="https://chrome.google.com/webstore/detail/oepplnnfceidfaaacjpdpobnjkcpgcpo" target="_blank">click here</a><br />
If you're using Firefox, the extension will be available soon.
<!-- <a href="" target="_blank">click here</a><br /> -->
</p>

<br />
<h2>Signing In and Getting Started</h2>
<div class="tutorial-image">
<img src="assets/tutorial/webextension-login.png" />
</div>
<p>
After installing the extension, you'll notice a new icon next to your navbar as pictured above.
The first time you click that icon, you'll be prompted to sign in to your RecipeSage account with your RecipeSage credentials.<br />
You'll then be shown a short tutorial, and the clip tool will open on the current page.
After this, clicking the RecipeSage icon will launch the clip tool immediately.<br /><br />

Note: If you don't have a RecipeSage account, you'll need to create one (see "Register" in the sidebar).<br />
Note: The clip tool cannot be activated on certain websites, such as browser settings pages and websites with certain security policies.
</p>

<br />
<h2>Using the Clip Tool</h2>
<div class="tutorial-image">
<img src="assets/tutorial/webextension-body.png" />
</div>
<p>
This is the clip tool. It allows you to quickly select text from a webpage and save recipes directly to your account.<br /><br />
To use it, highlight some text on the page using your mouse cursor, then press the button next to the desired field.<br />
The text you've selected will be copied into that field, where you can edit or refine the text.<br /><br />

To snip an image from the page, the process is slightly different - right click an image on the page,
and select the "Snip Image" option. The image source URL will be copied to the image URL field.<br /><br />

After you're done filling in the desired fields, press the "Save" button.
A popup notification will let you know if the recipe saved successfully or not.
If the recipe saved successfully, you'll receive a popup with a link to open the recipe in the full view.<br /><br />

Note: Only the title field is required.<br />
Note: Not all images will be snippable. If you're having trouble with an image, try creating your recipe first, then
edit the recipe on RecipeSage.com and add the image afterwards.<br />
Note: The clip tool is draggable. Click and hold on the icon in the upper left corner to move it anywhere within
the page.
</p>
</div>

<br /><br />

<button float-left ion-button clear (click)="active = ''">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ export class TipsTricksTutorialsPage {
description: 'Individual recipe view',
icon: 'paper',
tag: 'recipeDetails'
}, {
title: 'Clip Tool Browser Extension',
description: 'The RecipeSage browser plugin',
icon: 'cut',
tag: 'webExtension'
}];

this.tutorialsByTag = {};
Expand Down
61 changes: 37 additions & 24 deletions Frontend/src/pages/recipe-components/edit-recipe/edit-recipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,17 @@ export class EditRecipePage {

setFile(event) {
let files = event.srcElement.files
if (!files) {
if (!files || !files[0]) {
return
}

let MAX_FILE_SIZE_MB = 4;
if (files[0].size / 1024 / 1024 > MAX_FILE_SIZE_MB) {
// Image is larger than MAX_FILE_SIZE_MB
this.toastCtrl.create({
message: `The max image file size is ${MAX_FILE_SIZE_MB}MB. Please select a smaller image.`,
duration: 6000
}).present();
return
}

Expand All @@ -59,29 +69,32 @@ export class EditRecipePage {
(window.URL || (<any>window).webkitURL).createObjectURL(this.recipe.imageFile)
);

var loadingImage = loadImage(
files[0],
(renderedCanvas, exif) => {
renderedCanvas.toBlob(myBlob => {
myBlob.name = this.recipe.imageFile.name;
this.recipe.imageFile = myBlob;
this.imageBlobURL = this.domSanitizationService.bypassSecurityTrustUrl(
(window.URL || (<any>window).webkitURL).createObjectURL(this.recipe.imageFile)
);

console.log('Local conversion complete');
}, 'image/jpeg', 1);
},
{
maxWidth: 200,
canvas: true,
orientation: true
}
);

loadingImage.onerror = err => {
console.log('Local conversion failed', err)
};
let ENABLE_LOCAL_CONVERSIONS = false;
if (ENABLE_LOCAL_CONVERSIONS) {
var loadingImage = loadImage(
files[0],
(renderedCanvas, exif) => {
renderedCanvas.toBlob(myBlob => {
myBlob.name = this.recipe.imageFile.name;
this.recipe.imageFile = myBlob;
this.imageBlobURL = this.domSanitizationService.bypassSecurityTrustUrl(
(window.URL || (<any>window).webkitURL).createObjectURL(this.recipe.imageFile)
);

console.log('Local conversion complete');
}, 'image/jpeg', 1);
},
{
maxWidth: 200,
canvas: true,
orientation: true
}
);

loadingImage.onerror = err => {
console.log('Local conversion failed', err)
};
}
}

save() {
Expand Down
Binary file added WebExtension/1.0.1-1.zip
Binary file not shown.
Binary file added WebExtension/1.0.1-2.zip
Binary file not shown.
Binary file added WebExtension/1.0.1-3.zip
Binary file not shown.
Binary file added WebExtension/1.0.1.zip
Binary file not shown.
Binary file added WebExtension/icons/android-chrome-192x192.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 WebExtension/icons/android-chrome-512x512.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 WebExtension/icons/favicon-16x16.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 WebExtension/icons/favicon-32x32.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 WebExtension/icons/icon-128x128.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 WebExtension/icons/icon-48x48.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 WebExtension/images/recipesage-black-trimmed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 35 additions & 0 deletions WebExtension/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "RecipeSage",
"version": "1.0.1",
"manifest_version": 2,
"description": "The RecipeSage browser extension",
"homepage_url": "https://recipesage.com",
"icons": {
"16": "icons/favicon-16x16.png",
"48": "icons/icon-48x48.png",
"128": "icons/icon-128x128.png"
},
"background": {
"scripts": [
"src/bg/background.js"
],
"persistent": true
},
"browser_action": {
"default_icon": "icons/android-chrome-512x512.png",
"default_title": "RecipeSage",
"default_popup": "src/browser_action/browser_action.html"
},
"permissions": [
"storage",
"contextMenus",
"activeTab"
],

"web_accessible_resources": [
"images/*.png",
"icons/*.png",
"src/inject/*.css"
],
"options_page": "src/settings/settings.html"
}
52 changes: 52 additions & 0 deletions WebExtension/src/bg/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
let initClipTool = () => {
chrome.tabs.executeScript({
file: 'src/inject/inject.js'
});
}

let messageActiveWindow = payload => {
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, payload);
});
}

chrome.contextMenus.create({
title: "Snip Image for RecipeSage Clip Tool",
contexts: ['image'],
onclick: e => {
initClipTool();
messageActiveWindow({
action: 'snipImage',
event: e
});
}
});

chrome.contextMenus.create({
title: "Open RecipeSage Clip Tool",
contexts: ['page'],
onclick: e => {
initClipTool();
messageActiveWindow({
action: 'show',
event: e
});
}
});

// Extend the current token if it exists
let renewToken = () => {
chrome.storage.local.get(['token'], result => {
token = result.token;

if (token) fetch(`https://recipesage.com/api/users/sessioncheck?token=${token}`).then(response => {
if (!response.ok && response.status == 401) {
chrome.storage.local.set({ token: null });
}
});
});
}

setInterval(renewToken, 1000 * 60 * 60 * 24); // Once per 24 hours

setTimeout(renewToken); // Avoid locking render loop
Loading

0 comments on commit 4591931

Please sign in to comment.