diff --git a/src/components/Login.js b/src/components/Login.js index 62ad6a0..c93929d 100644 --- a/src/components/Login.js +++ b/src/components/Login.js @@ -1,4 +1,5 @@ 'use strict'; + const React = require('react'); const create = require('create-react-class'); const Promise = require('bluebird'); diff --git a/src/components/chat.js b/src/components/chat.js index 91631b2..23ff2e6 100644 --- a/src/components/chat.js +++ b/src/components/chat.js @@ -1,7 +1,6 @@ 'use strict'; const React = require('react'); const create = require('create-react-class'); -const jdenticon = require('jdenticon'); const sanitize = require('sanitize-html'); const Event = require('./events/Event.js'); @@ -10,17 +9,7 @@ const Input = require('./input.js'); const User = require('./events/user.js'); const Loading = require('./loading.js'); -jdenticon.config = { - lightness: { - color: [0.58, 0.66], - grayscale: [0.30, 0.90] - }, - saturation: { - color: 0.66, - grayscale: 0.00 - }, - backColor: "#00000000" -}; +const generateJdenticon = require("../lib/generate-jdenticon"); let eventFunctions = { plaintext: function() { @@ -202,7 +191,7 @@ let EventGroup = create({ }, avatarRef: function(ref) { - jdenticon.update(ref, this.state.user.userId); + generateJdenticon(this.state.user.userId).update(ref); }, render: function() { diff --git a/src/components/events/user.js b/src/components/events/user.js index b63aa13..3f1bcfe 100644 --- a/src/components/events/user.js +++ b/src/components/events/user.js @@ -1,39 +1,17 @@ -'use strict'; -const React = require('react'); -const create = require('create-react-class'); -const jdenticon = require('jdenticon'); +"use strict"; -jdenticon.config = { - lightness: { - color: [0.58, 0.66], - grayscale: [0.30, 0.90] - }, - saturation: { - color: 0.66, - grayscale: 0.00 - }, - backColor: "#00000000" -}; +const React = require("react"); +const create = require("create-react-class"); + +const generateJdenticon = require("../../lib/generate-jdenticon"); let User = create({ displayName: "user", getInitialState: function() { - let icon = jdenticon.toSvg(this.props.user.userId, 200); - let match = icon.match(/#([a-f0-9]{6})/g); - let color = '#ff0000'; - /* FIXME: Replace with a .find */ - for(let i=match.length-1; i>= 0; i--) { - color = match[i]; - let r = color.substr(1, 2); - let g = color.substr(3, 2); - let b = color.substr(5, 2); - if (r != g && g != b) { // not greyscale - break; - } - } return { - color: color + /* FIXME: Cache this to speed it up */ + color: generateJdenticon(this.props.user.userId).primaryColor() }; }, diff --git a/src/lib/api-request.js b/src/lib/api-request.js index a5a5fed..9658892 100644 --- a/src/lib/api-request.js +++ b/src/lib/api-request.js @@ -23,5 +23,5 @@ module.exports = function createApiRequester(apiUrl) { } }); }); - } + }; }; diff --git a/src/lib/riot-utils.js b/src/lib/riot-utils.js index a80ad8c..7bb4b19 100644 --- a/src/lib/riot-utils.js +++ b/src/lib/riot-utils.js @@ -1,6 +1,9 @@ 'use strict'; /* Copyright 2015, 2016 OpenMarket Ltd +With modifications, by f0x +With modifications, by Sven Slootweg + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -14,7 +17,6 @@ limitations under the License. const Promise = require('bluebird'); const sanitize = require('sanitize-html'); -//require("blueimp-canvas-to-blob"); const COLOR_REGEX = /^#[0-9a-fA-F]{6}$/; /** @@ -36,29 +38,86 @@ const COLOR_REGEX = /^#[0-9a-fA-F]{6}$/; * and a thumbnail key. */ +function canvasToBlob(canvas, mimeType) { + return new Promise((resolve, _reject) => { + canvas.toBlob(function(blob) { + resolve(blob); + }, mimeType); + }); +} + +function awaitImageLoad(image) { + return new Promise((resolve, reject) => { + image.onload = function() { + resolve(); + }; + image.onerror = function(e) { + reject(e); + }; + }); +} + +function awaitFileReader(dataUrl) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + + reader.onload = function(event) { + resolve(event.target.result); + }; + + reader.onerror = function(error) { + reject(error); + }; + + reader.readAsDataURL(dataUrl); + }); +} + +function awaitVideoLoad(video) { + return new Promise((resolve, reject) => { + video.onloadeddata = function() { + resolve({ + width: video.videoWidth, + height: video.videoHeight + }); + }; + + video.onerror = function(error) { + reject(error); + }; + }); +} + module.exports = { createThumbnail: function(element, inputWidth, inputHeight, mimeType) { - return new Promise(function(resolve, reject) { + return Promise.try(() => { const MAX_WIDTH = 800; const MAX_HEIGHT = 600; - + let targetWidth = inputWidth; let targetHeight = inputHeight; + if (targetHeight > MAX_HEIGHT) { targetWidth = Math.floor(targetWidth * (MAX_HEIGHT / targetHeight)); targetHeight = MAX_HEIGHT; } + if (targetWidth > MAX_WIDTH) { targetHeight = Math.floor(targetHeight * (MAX_WIDTH / targetWidth)); targetWidth = MAX_WIDTH; } + + const canvas = Object.assign(document.createElement("canvas"), { + width: targetWidth, + height: targetHeight + }); - const canvas = document.createElement("canvas"); - canvas.width = targetWidth; - canvas.height = targetHeight; canvas.getContext("2d").drawImage(element, 0, 0, targetWidth, targetHeight); - canvas.toBlob(function(thumbnail) { - resolve({ + + return Promise.try(() => { + return canvasToBlob(canvas, mimeType); + }).then((thumbnail) => { + return { info: { thumbnail_info: { w: targetWidth, @@ -70,8 +129,8 @@ module.exports = { h: inputHeight, }, thumbnail: thumbnail, - }); - }, mimeType); + }; + }); }); }, @@ -82,20 +141,16 @@ module.exports = { * @return {Promise} A promise that resolves with the html image element. */ loadImageElement: function(imageFile) { - return new Promise(function(resolve, reject) { - // Load the file into an html element + return Promise.try(() => { const img = document.createElement("img"); const objectUrl = URL.createObjectURL(imageFile); img.src = objectUrl; - - // Once ready, create a thumbnail - img.onload = function() { + + return Promise.try(() => { + return awaitImageLoad(img); + }).then(() => { URL.revokeObjectURL(objectUrl); - resolve(img); - }; - img.onerror = function(e) { - reject(e); - }; + }); }); }, @@ -106,29 +161,19 @@ module.exports = { * @return {Promise} A promise that resolves with the video image element. */ loadVideoElement: function(videoFile) { - return new Promise(function(resolve, reject) { - // Load the file into an html element - const video = document.createElement("video"); - - const reader = new FileReader(); - reader.onload = function(e) { - video.src = e.target.result; - - // Once ready, returns its size - // Wait until we have enough data to thumbnail the first frame. - video.onloadeddata = function() { - video.width = video.videoWidth; - video.height = video.videoHeight; - resolve(video); - }; - video.onerror = function(e) { - reject(e); - }; - }; - reader.onerror = function(e) { - reject(e); - }; - reader.readAsDataURL(videoFile); + return Promise.try(() => { + return awaitFileReader(videoFile); + }).then((url) => { + const video = Object.assign(document.createElement("video"), { + src: url + }); + + return Promise.try(() => { + return awaitVideoLoad(video); + }).then((dimensions) => { + /* FIXME: Check whether this can be improved, it's a bit dirty to shoehorn the dimensions onto the video object like this */ + return Object.assign(video, dimensions); + }); }); }, @@ -163,7 +208,7 @@ module.exports = { transformTags: { // custom to matrix // add blank targets to all hyperlinks except vector URLs - 'img': function(tagName, attribs) { + 'img': function(tagName, _attribs) { // Strip out imgs that aren't `mxc` here instead of using allowedSchemesByTag // because transformTags is used _before_ we filter by allowedSchemesByTag and // we don't want to allow images with `https?` `src`s. @@ -209,10 +254,7 @@ module.exports = { Object.keys(customCSSMapper).forEach((customAttributeKey) => { const cssAttributeKey = customCSSMapper[customAttributeKey]; const customAttributeValue = attribs[customAttributeKey]; - if (customAttributeValue && - typeof customAttributeValue === 'string' && - COLOR_REGEX.test(customAttributeValue) - ) { + if (customAttributeValue && typeof customAttributeValue === 'string' && COLOR_REGEX.test(customAttributeValue)) { style += cssAttributeKey + ":" + customAttributeValue + ";"; delete attribs[customAttributeKey]; }