diff --git a/src/Login.js b/src/Login.js index 55e996ce80..61a14959d8 100644 --- a/src/Login.js +++ b/src/Login.js @@ -204,6 +204,12 @@ export default class Login { } throw originalLoginError; }).catch((error) => { + // We apparently squash case at login serverside these days: + // https://github.com/matrix-org/synapse/blob/1189be43a2479f5adf034613e8d10e3f4f452eb9/synapse/handlers/auth.py#L475 + // so this wasn't needed after all. Keeping the code around in case the + // the situation changes... + + /* if ( error.httpStatus === 403 && loginParams.identifier.type === 'm.id.user' && @@ -211,6 +217,7 @@ export default class Login { ) { return tryLowercaseUsername(originalLoginError); } + */ throw originalLoginError; }).catch((error) => { console.log("Login failed", error); diff --git a/src/SdkConfig.js b/src/SdkConfig.js index 48ebf011f2..8df725a913 100644 --- a/src/SdkConfig.js +++ b/src/SdkConfig.js @@ -26,7 +26,7 @@ const DEFAULTS = { class SdkConfig { static get() { - return global.mxReactSdkConfig; + return global.mxReactSdkConfig || {}; } static put(cfg) { diff --git a/src/Tinter.js b/src/Tinter.js index 6b23df8c9b..fe4cafe744 100644 --- a/src/Tinter.js +++ b/src/Tinter.js @@ -1,5 +1,6 @@ /* Copyright 2015 OpenMarket Ltd +Copyright 2017 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,125 +15,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -// FIXME: these vars should be bundled up and attached to -// module.exports otherwise this will break when included by both -// react-sdk and apps layered on top. - const DEBUG = 0; -// The colour keys to be replaced as referred to in CSS -const keyRgb = [ - "rgb(118, 207, 166)", // Vector Green - "rgb(234, 245, 240)", // Vector Light Green - "rgb(211, 239, 225)", // BottomLeftMenu overlay (20% Vector Green) -]; - -// Some algebra workings for calculating the tint % of Vector Green & Light Green -// x * 118 + (1 - x) * 255 = 234 -// x * 118 + 255 - 255 * x = 234 -// x * 118 - x * 255 = 234 - 255 -// (255 - 118) x = 255 - 234 -// x = (255 - 234) / (255 - 118) = 0.16 - -// The colour keys to be replaced as referred to in SVGs -const keyHex = [ - "#76CFA6", // Vector Green - "#EAF5F0", // Vector Light Green - "#D3EFE1", // BottomLeftMenu overlay (20% Vector Green overlaid on Vector Light Green) - "#FFFFFF", // white highlights of the SVGs (for switching to dark theme) -]; - -// cache of our replacement colours -// defaults to our keys. -const colors = [ - keyHex[0], - keyHex[1], - keyHex[2], - keyHex[3], -]; - -const cssFixups = [ - // { - // style: a style object that should be fixed up taken from a stylesheet - // attr: name of the attribute to be clobbered, e.g. 'color' - // index: ordinal of primary, secondary or tertiary - // } -]; - -// CSS attributes to be fixed up -const cssAttrs = [ - "color", - "backgroundColor", - "borderColor", - "borderTopColor", - "borderBottomColor", - "borderLeftColor", -]; - -const svgAttrs = [ - "fill", - "stroke", -]; - -let cached = false; - -function calcCssFixups() { - if (DEBUG) console.log("calcSvgFixups start"); - for (let i = 0; i < document.styleSheets.length; i++) { - const ss = document.styleSheets[i]; - if (!ss) continue; // well done safari >:( - // Chromium apparently sometimes returns null here; unsure why. - // see $14534907369972FRXBx:matrix.org in HQ - // ...ah, it's because there's a third party extension like - // privacybadger inserting its own stylesheet in there with a - // resource:// URI or something which results in a XSS error. - // See also #vector:matrix.org/$145357669685386ebCfr:matrix.org - // ...except some browsers apparently return stylesheets without - // hrefs, which we have no choice but ignore right now - - // XXX seriously? we are hardcoding the name of vector's CSS file in - // here? - // - // Why do we need to limit it to vector's CSS file anyway - if there - // are other CSS files affecting the doc don't we want to apply the - // same transformations to them? - // - // Iterating through the CSS looking for matches to hack on feels - // pretty horrible anyway. And what if the application skin doesn't use - // Vector Green as its primary color? - - if (ss.href && !ss.href.match(/\/bundle.*\.css$/)) continue; - - if (!ss.cssRules) continue; - for (let j = 0; j < ss.cssRules.length; j++) { - const rule = ss.cssRules[j]; - if (!rule.style) continue; - for (let k = 0; k < cssAttrs.length; k++) { - const attr = cssAttrs[k]; - for (let l = 0; l < keyRgb.length; l++) { - if (rule.style[attr] === keyRgb[l]) { - cssFixups.push({ - style: rule.style, - attr: attr, - index: l, - }); - } - } - } - } - } - if (DEBUG) console.log("calcSvgFixups end"); -} - -function applyCssFixups() { - if (DEBUG) console.log("applyCssFixups start"); - for (let i = 0; i < cssFixups.length; i++) { - const cssFixup = cssFixups[i]; - cssFixup.style[cssFixup.attr] = colors[cssFixup.index]; - } - if (DEBUG) console.log("applyCssFixups end"); -} - +// utility to turn #rrggbb into [red,green,blue] function hexToRgb(color) { if (color[0] === '#') color = color.slice(1); if (color.length === 3) { @@ -147,15 +32,77 @@ function hexToRgb(color) { return [r, g, b]; } +// utility to turn [red,green,blue] into #rrggbb function rgbToHex(rgb) { const val = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; return '#' + (0x1000000 + val).toString(16).slice(1); } -// List of functions to call when the tint changes. -const tintables = []; +class Tinter { + constructor() { + // The default colour keys to be replaced as referred to in CSS + // (should be overridden by .mx_theme_accentColor and .mx_theme_secondaryAccentColor) + this.keyRgb = [ + "rgb(118, 207, 166)", // Vector Green + "rgb(234, 245, 240)", // Vector Light Green + "rgb(211, 239, 225)", // Unused: BottomLeftMenu (20% Green overlaid on Light Green) + ]; + + // Some algebra workings for calculating the tint % of Vector Green & Light Green + // x * 118 + (1 - x) * 255 = 234 + // x * 118 + 255 - 255 * x = 234 + // x * 118 - x * 255 = 234 - 255 + // (255 - 118) x = 255 - 234 + // x = (255 - 234) / (255 - 118) = 0.16 + + // The colour keys to be replaced as referred to in SVGs + this.keyHex = [ + "#76CFA6", // Vector Green + "#EAF5F0", // Vector Light Green + "#D3EFE1", // Unused: BottomLeftMenu (20% Green overlaid on Light Green) + "#FFFFFF", // white highlights of the SVGs (for switching to dark theme) + ]; + + // cache of our replacement colours + // defaults to our keys. + this.colors = [ + this.keyHex[0], + this.keyHex[1], + this.keyHex[2], + this.keyHex[3], + ]; + + this.cssFixups = [ + // { theme: { + // style: a style object that should be fixed up taken from a stylesheet + // attr: name of the attribute to be clobbered, e.g. 'color' + // index: ordinal of primary, secondary or tertiary + // }, + // } + ]; + + // CSS attributes to be fixed up + this.cssAttrs = [ + "color", + "backgroundColor", + "borderColor", + "borderTopColor", + "borderBottomColor", + "borderLeftColor", + ]; + + this.svgAttrs = [ + "fill", + "stroke", + ]; + + // List of functions to call when the tint changes. + this.tintables = []; + + // the currently loaded theme (if any) + this.theme = undefined; + } -module.exports = { /** * Register a callback to fire when the tint changes. * This is used to rewrite the tintable SVGs with the new tint. @@ -167,19 +114,24 @@ module.exports = { * * @param {Function} tintable Function to call when the tint changes. */ - registerTintable: function(tintable) { - tintables.push(tintable); - }, + registerTintable(tintable) { + this.tintables.push(tintable); + } - tint: function(primaryColor, secondaryColor, tertiaryColor) { - if (!cached) { - calcCssFixups(); - cached = true; - } + getKeyRgb() { + return this.keyRgb; + } + + getCurrentColors() { + return this.colors; + } + + tint(primaryColor, secondaryColor, tertiaryColor) { + this.calcCssFixups(); if (!primaryColor) { - primaryColor = "#76CFA6"; // Vector green - secondaryColor = "#EAF5F0"; // Vector light green + primaryColor = this.keyRgb[0]; + secondaryColor = this.keyRgb[1]; } if (!secondaryColor) { @@ -201,45 +153,141 @@ module.exports = { tertiaryColor = rgbToHex(rgb1); } - if (colors[0] === primaryColor && - colors[1] === secondaryColor && - colors[2] === tertiaryColor) { + if (this.colors[0] === primaryColor && + this.colors[1] === secondaryColor && + this.colors[2] === tertiaryColor) { return; } - colors[0] = primaryColor; - colors[1] = secondaryColor; - colors[2] = tertiaryColor; + this.colors[0] = primaryColor; + this.colors[1] = secondaryColor; + this.colors[2] = tertiaryColor; if (DEBUG) console.log("Tinter.tint"); // go through manually fixing up the stylesheets. - applyCssFixups(); + this.applyCssFixups(); // tell all the SVGs to go fix themselves up // we don't do this as a dispatch otherwise it will visually lag - tintables.forEach(function(tintable) { + this.tintables.forEach(function(tintable) { tintable(); }); - }, + } - tintSvgWhite: function(whiteColor) { + tintSvgWhite(whiteColor) { if (!whiteColor) { - whiteColor = colors[3]; + whiteColor = this.colors[3]; } - if (colors[3] === whiteColor) { + if (this.colors[3] === whiteColor) { return; } - colors[3] = whiteColor; - tintables.forEach(function(tintable) { + this.colors[3] = whiteColor; + this.tintables.forEach(function(tintable) { tintable(); }); - }, + } + + setTheme(theme) { + this.theme = theme; + + // update keyRgb from the current theme CSS itself, if it defines it + if (document.getElementById('mx_theme_accentColor')) { + this.keyRgb[0] = window.getComputedStyle( + document.getElementById('mx_theme_accentColor') + ).color; + } + if (document.getElementById('mx_theme_secondaryAccentColor')) { + this.keyRgb[1] = window.getComputedStyle( + document.getElementById('mx_theme_secondaryAccentColor') + ).color; + } + + this.calcCssFixups(); + } + + calcCssFixups() { + // cache our fixups + if (this.cssFixups[this.theme]) return; + + if (DEBUG) console.trace("calcCssFixups start for " + this.theme + " (checking " + + document.styleSheets.length + + " stylesheets)"); + + this.cssFixups[this.theme] = []; + + for (let i = 0; i < document.styleSheets.length; i++) { + const ss = document.styleSheets[i]; + if (!ss) continue; // well done safari >:( + // Chromium apparently sometimes returns null here; unsure why. + // see $14534907369972FRXBx:matrix.org in HQ + // ...ah, it's because there's a third party extension like + // privacybadger inserting its own stylesheet in there with a + // resource:// URI or something which results in a XSS error. + // See also #vector:matrix.org/$145357669685386ebCfr:matrix.org + // ...except some browsers apparently return stylesheets without + // hrefs, which we have no choice but ignore right now + + // XXX seriously? we are hardcoding the name of vector's CSS file in + // here? + // + // Why do we need to limit it to vector's CSS file anyway - if there + // are other CSS files affecting the doc don't we want to apply the + // same transformations to them? + // + // Iterating through the CSS looking for matches to hack on feels + // pretty horrible anyway. And what if the application skin doesn't use + // Vector Green as its primary color? + // --richvdh + + // Yes, tinting assumes that you are using the Riot skin for now. + // The right solution will be to move the CSS over to react-sdk. + // And yes, the default assets for the base skin might as well use + // Vector Green as any other colour. + // --matthew + + if (ss.href && !ss.href.match(new RegExp('/theme-' + this.theme + '.css$'))) continue; + if (ss.disabled) continue; + if (!ss.cssRules) continue; + + for (let j = 0; j < ss.cssRules.length; j++) { + const rule = ss.cssRules[j]; + if (!rule.style) continue; + if (rule.selectorText && rule.selectorText.match(/#mx_theme/)) continue; + for (let k = 0; k < this.cssAttrs.length; k++) { + const attr = this.cssAttrs[k]; + for (let l = 0; l < this.keyRgb.length; l++) { + if (rule.style[attr] === this.keyRgb[l]) { + this.cssFixups[this.theme].push({ + style: rule.style, + attr: attr, + index: l, + }); + } + } + } + } + } + if (DEBUG) console.log("calcCssFixups end (" + + this.cssFixups[this.theme].length + + " fixups)"); + } + + applyCssFixups() { + if (DEBUG) console.log("applyCssFixups start (" + + this.cssFixups[this.theme].length + + " fixups)"); + for (let i = 0; i < this.cssFixups[this.theme].length; i++) { + const cssFixup = this.cssFixups[this.theme][i]; + cssFixup.style[cssFixup.attr] = this.colors[cssFixup.index]; + } + if (DEBUG) console.log("applyCssFixups end"); + } // XXX: we could just move this all into TintableSvg, but as it's so similar // to the CSS fixup stuff in Tinter (just that the fixups are stored in TintableSvg) // keeping it here for now. - calcSvgFixups: function(svgs) { + calcSvgFixups(svgs) { // go through manually fixing up SVG colours. // we could do this by stylesheets, but keeping the stylesheets // updated would be a PITA, so just brute-force search for the @@ -265,10 +313,12 @@ module.exports = { const tags = svgDoc.getElementsByTagName("*"); for (let j = 0; j < tags.length; j++) { const tag = tags[j]; - for (let k = 0; k < svgAttrs.length; k++) { - const attr = svgAttrs[k]; - for (let l = 0; l < keyHex.length; l++) { - if (tag.getAttribute(attr) && tag.getAttribute(attr).toUpperCase() === keyHex[l]) { + for (let k = 0; k < this.svgAttrs.length; k++) { + const attr = this.svgAttrs[k]; + for (let l = 0; l < this.keyHex.length; l++) { + if (tag.getAttribute(attr) && + tag.getAttribute(attr).toUpperCase() === this.keyHex[l]) + { fixups.push({ node: tag, attr: attr, @@ -282,14 +332,19 @@ module.exports = { if (DEBUG) console.log("calcSvgFixups end"); return fixups; - }, + } - applySvgFixups: function(fixups) { + applySvgFixups(fixups) { if (DEBUG) console.log("applySvgFixups start for " + fixups); for (let i = 0; i < fixups.length; i++) { const svgFixup = fixups[i]; - svgFixup.node.setAttribute(svgFixup.attr, colors[svgFixup.index]); + svgFixup.node.setAttribute(svgFixup.attr, this.colors[svgFixup.index]); } if (DEBUG) console.log("applySvgFixups end"); - }, -}; + } +} + +if (global.singletonTinter === undefined) { + global.singletonTinter = new Tinter(); +} +export default global.singletonTinter; diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js index 34f9a28d57..8d60790812 100644 --- a/src/UserSettingsStore.js +++ b/src/UserSettingsStore.js @@ -176,6 +176,20 @@ export default { }); }, + getTheme: function() { + let syncedSettings; + let theme; + if (MatrixClientPeg.get()) { + syncedSettings = this.getSyncedSettings(); + } + if (!syncedSettings || !syncedSettings.theme) { + theme = (SdkConfig.get() ? SdkConfig.get().default_theme : undefined) || 'light'; + } else { + theme = syncedSettings.theme; + } + return theme; + }, + getSyncedSettings: function() { const event = MatrixClientPeg.get().getAccountData('im.vector.web.settings'); return event ? event.getContent() : {}; diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index c201d139c5..268654103d 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -287,6 +287,9 @@ module.exports = React.createClass({ this._windowWidth = 10000; this.handleResize(); window.addEventListener('resize', this.handleResize); + + // check we have the right tint applied for this theme + Tinter.tint(); }, componentDidMount: function() { @@ -883,7 +886,7 @@ module.exports = React.createClass({ */ _onSetTheme: function(theme) { if (!theme) { - theme = 'light'; + theme = this.props.config.default_theme || 'light'; } // look for the stylesheet elements. @@ -912,6 +915,10 @@ module.exports = React.createClass({ }); styleElements[theme].disabled = false; + Tinter.setTheme(theme); + const colors = Tinter.getCurrentColors(); + Tinter.tint(colors[0], colors[1]); + if (theme === 'dark') { // abuse the tinter to change all the SVG's #fff to #2d2d2d // XXX: obviously this shouldn't be hardcoded here. diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 68ea932f93..f28f9a834f 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -183,6 +183,11 @@ const THEMES = [ label: _td('Dark theme'), value: 'dark', }, + { + id: 'theme', + label: _td('Status.im theme'), + value: 'status', + }, ]; const IgnoredUser = React.createClass({ @@ -204,7 +209,7 @@ const IgnoredUser = React.createClass({ render: function() { return (
{ _t('Your password has been reset') }.
{ _t('You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device') }.
); } else { + let serverConfigSection; + if (!config.disable_custom_urls) { + serverConfigSection = ( +This channel is for our development community.
+Interested in SNT and discussions on the cryptocurrency market?
+ +