diff --git a/app/controller.js b/app/controller.js index 6648f85b..c7c0769c 100644 --- a/app/controller.js +++ b/app/controller.js @@ -1,13 +1,14 @@ -import FileSender from './fileSender'; -import FileReceiver from './fileReceiver'; -import { copyToClipboard, delay, openLinksInNewTab, percent } from './utils'; import * as metrics from './metrics'; -import { bytes, locale } from './utils'; -import okDialog from './ui/okDialog'; +import FileReceiver from './fileReceiver'; +import FileSender from './fileSender'; import copyDialog from './ui/copyDialog'; +import faviconProgressbar from './ui/faviconProgressbar'; +import okDialog from './ui/okDialog'; import shareDialog from './ui/shareDialog'; import signupDialog from './ui/signupDialog'; import surveyDialog from './ui/surveyDialog'; +import { bytes, locale } from './utils'; +import { copyToClipboard, delay, openLinksInNewTab, percent } from './utils'; export default function(state, emitter) { let lastRender = 0; @@ -29,6 +30,7 @@ export default function(state, emitter) { if (updateTitle) { emitter.emit('DOMTitleChange', percent(state.transfer.progressRatio)); } + faviconProgressbar.updateFavicon(state.transfer.progressRatio); render(); } @@ -37,6 +39,7 @@ export default function(state, emitter) { document.addEventListener('focus', () => { updateTitle = false; emitter.emit('DOMTitleChange', 'Send'); + faviconProgressbar.updateFavicon(0); }); checkFiles(); }); @@ -83,6 +86,7 @@ export default function(state, emitter) { emitter.on('cancel', () => { state.transfer.cancel(); + faviconProgressbar.updateFavicon(0); }); emitter.on('addFiles', async ({ files }) => { @@ -161,6 +165,7 @@ export default function(state, emitter) { state.storage.totalUploads += 1; const duration = Date.now() - start; metrics.completedUpload(archive, duration); + faviconProgressbar.updateFavicon(0); state.storage.addFile(ownedFile); // TODO integrate password into /upload request @@ -264,6 +269,7 @@ export default function(state, emitter) { duration, password_protected: file.requiresPassword }); + faviconProgressbar.updateFavicon(0); } catch (err) { if (err.message === '0') { // download cancelled diff --git a/app/ui/faviconProgressbar.js b/app/ui/faviconProgressbar.js new file mode 100644 index 00000000..7967f5a3 --- /dev/null +++ b/app/ui/faviconProgressbar.js @@ -0,0 +1,41 @@ +const { platform } = require('../utils'); +const assets = require('../../common/assets'); + +const size = 32; +const loaderWidth = 5; +const loaderColor = '#0090ed'; + +function drawCircle(canvas, context, color, lineWidth, outerWidth, percent) { + canvas.width = canvas.height = outerWidth; + context.translate(outerWidth * 0.5, outerWidth * 0.5); + context.rotate(-Math.PI * 0.5); + const radius = (outerWidth - lineWidth) * 0.5; + context.beginPath(); + context.arc(0, 0, radius, 0, Math.PI * 2 * percent, false); + context.strokeStyle = color; + context.lineCap = 'square'; + context.lineWidth = lineWidth; + context.stroke(); +} + +function drawNewFavicon(progressRatio) { + const canvas = document.createElement('canvas'); + const context = canvas.getContext('2d'); + drawCircle(canvas, context, '#efefef', loaderWidth, size, 1); + drawCircle(canvas, context, loaderColor, loaderWidth, size, progressRatio); + return canvas.toDataURL(); +} + +module.exports.updateFavicon = function(progressRatio) { + if (platform() === 'web') { + const link = document.querySelector("link[rel='icon'][sizes='32x32']"); + const progress = progressRatio * 100; + if (progress === 0 || progress === 100) { + link.type = 'image/png'; + link.href = assets.get('favicon-32x32.png'); + return; + } + + link.href = drawNewFavicon(progressRatio); + } +};