From 2f68f608c5dc58c187c4c7f16a28f4c96b4864ff Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 27 Apr 2020 17:30:28 +0200 Subject: [PATCH 1/7] add 15pct value of accent color for FormButton backgrounds so the button in the toasts has the correct background color --- res/themes/light-custom/css/_custom.scss | 1 + src/theme.js | 1 + 2 files changed, 2 insertions(+) diff --git a/res/themes/light-custom/css/_custom.scss b/res/themes/light-custom/css/_custom.scss index e4a08277f9..505fc3733b 100644 --- a/res/themes/light-custom/css/_custom.scss +++ b/res/themes/light-custom/css/_custom.scss @@ -17,6 +17,7 @@ limitations under the License. // // --accent-color $accent-color: var(--accent-color); +$accent-bg-color: var(--accent-color-15pct); $button-bg-color: var(--accent-color); $button-link-fg-color: var(--accent-color); $button-primary-bg-color: var(--accent-color); diff --git a/src/theme.js b/src/theme.js index 442a89e25f..6bd81cc021 100644 --- a/src/theme.js +++ b/src/theme.js @@ -148,6 +148,7 @@ function setCustomThemeVars(customTheme) { style.setProperty(`--${name}`, hexColor); // uses #rrggbbaa to define the color with alpha values at 0% and 50% style.setProperty(`--${name}-0pct`, hexColor + "00"); + style.setProperty(`--${name}-15pct`, hexColor + "26"); style.setProperty(`--${name}-50pct`, hexColor + "7F"); } } From b1870660d8d9282cbd1a47e6b0a2c4e05d5dc230 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 27 Apr 2020 17:31:22 +0200 Subject: [PATCH 2/7] support theming username colors --- res/themes/light-custom/css/_custom.scss | 9 +++++++++ src/theme.js | 20 +++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/res/themes/light-custom/css/_custom.scss b/res/themes/light-custom/css/_custom.scss index 505fc3733b..64c4cac7e3 100644 --- a/res/themes/light-custom/css/_custom.scss +++ b/res/themes/light-custom/css/_custom.scss @@ -125,3 +125,12 @@ $notice-primary-color: var(--warning-color); $pinned-unread-color: var(--warning-color); $warning-color: var(--warning-color); $button-danger-disabled-bg-color: var(--warning-color-50pct); // still needs alpha at 0.5 + +$username-variant1-color: var(--username-colors_1, $username-variant1-color); +$username-variant2-color: var(--username-colors_2, $username-variant2-color); +$username-variant3-color: var(--username-colors_3, $username-variant3-color); +$username-variant4-color: var(--username-colors_4, $username-variant4-color); +$username-variant5-color: var(--username-colors_5, $username-variant5-color); +$username-variant6-color: var(--username-colors_6, $username-variant6-color); +$username-variant7-color: var(--username-colors_7, $username-variant7-color); +$username-variant8-color: var(--username-colors_8, $username-variant8-color); diff --git a/src/theme.js b/src/theme.js index 6bd81cc021..b7fbe40df4 100644 --- a/src/theme.js +++ b/src/theme.js @@ -141,17 +141,31 @@ export function enumerateThemes() { return Object.assign({}, customThemeNames, BUILTIN_THEMES); } + function setCustomThemeVars(customTheme) { const {style} = document.body; - if (customTheme.colors) { - for (const [name, hexColor] of Object.entries(customTheme.colors)) { - style.setProperty(`--${name}`, hexColor); + + function setCSSVariable(name, hexColor, doPct = true) { + style.setProperty(`--${name}`, hexColor); + if (doPct) { // uses #rrggbbaa to define the color with alpha values at 0% and 50% style.setProperty(`--${name}-0pct`, hexColor + "00"); style.setProperty(`--${name}-15pct`, hexColor + "26"); style.setProperty(`--${name}-50pct`, hexColor + "7F"); } } + + if (customTheme.colors) { + for (const [name, value] of Object.entries(customTheme.colors)) { + if (Array.isArray(value)) { + for (let i = 0; i < value.length; i += 1) { + setCSSVariable(`${name}_${i}`, value[i], false); + } + } else { + setCSSVariable(name, value); + } + } + } } function getCustomTheme(themeName) { From e8e99c2646851bd0fc2d380e41b25bd1c9ac3e53 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 27 Apr 2020 19:35:38 +0200 Subject: [PATCH 3/7] Use color values for default avatar backgrounds We still need to convert them to URLs for the notification icon, so use a canvas (with a cache in front of it) to do that. --- res/img/03b381.png | Bin 169 -> 0 bytes res/img/368bd6.png | Bin 171 -> 0 bytes res/img/ac3ba8.png | Bin 170 -> 0 bytes src/Avatar.js | 28 ++++++++++++++++++++++++++-- 4 files changed, 26 insertions(+), 2 deletions(-) delete mode 100644 res/img/03b381.png delete mode 100644 res/img/368bd6.png delete mode 100644 res/img/ac3ba8.png diff --git a/res/img/03b381.png b/res/img/03b381.png deleted file mode 100644 index cf28fc7e5972c64194cf0d3523f88ccc4029b0b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEEa{HEjtmSN`?>!lvI6-Do-U3d z5u9%?ZR9i7itjH?&t;ucLK6VpHaVC8 diff --git a/res/img/368bd6.png b/res/img/368bd6.png deleted file mode 100644 index a2700bd0aefa3f3240ba4bae80953d9968bd78e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEEa{HEjtmSN`?>!lvI6-@o-U3d z5u9%?ZR9 Q8PGNcPgg&ebxsLQ0Ia4vrT_o{ diff --git a/res/img/ac3ba8.png b/res/img/ac3ba8.png deleted file mode 100644 index 031471d85a1d9cf5ab90194d788c26a1b90f8de7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 170 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEEa{HEjtmSN`?>!lvI6;uo-U3d z5u9%?ZR9Sv=ZSPn8FZ?)L{C=6=YlpuJS%gCv-`-AW z(ppd}?GwPdCBK<-Rl~Pj14q#d`*_t>Fx}dIkR{aNx7myZTp9P-K#I*h>X_~YE;#!f PXc>d2tDnm{r-UW|94|Wi diff --git a/src/Avatar.js b/src/Avatar.js index 217b196348..52bdbc1a50 100644 --- a/src/Avatar.js +++ b/src/Avatar.js @@ -53,13 +53,37 @@ export function avatarUrlForUser(user, width, height, resizeMethod) { return url; } +function urlForColor(color) { + const size = 40; + const canvas = document.createElement("canvas"); + canvas.width = size; + canvas.height = size; + const ctx = canvas.getContext("2d"); + ctx.fillStyle = color; + ctx.fillRect(0, 0, size, size); + return canvas.toDataURL(); +} + +// XXX: Ideally we'd clear this cache when the theme changes +// but since this function is at global scope, it's a bit +// hard to install a listener here, even if there were a clear event to listen to +const colorToDataURLCache = new Map(); + export function defaultAvatarUrlForString(s) { - const images = ['03b381', '368bd6', 'ac3ba8']; + const defaultColors = ['#03b381', '#368bd6', '#ac3ba8']; let total = 0; for (let i = 0; i < s.length; ++i) { total += s.charCodeAt(i); } - return require('../res/img/' + images[total % images.length] + '.png'); + const colorIndex = total % defaultColors.length; + // overwritten color value in custom themes + const color = defaultColors[colorIndex]; + let dataUrl = colorToDataURLCache.get(color); + if (!dataUrl) { + dataUrl = urlForColor(color); + colorToDataURLCache.set(color, dataUrl); + } + return dataUrl; } /** From 196931000343e9fe1281ac0a480e8d35e24dbdf1 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 27 Apr 2020 19:38:27 +0200 Subject: [PATCH 4/7] support overriding avatar background color from custom theme --- src/Avatar.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Avatar.js b/src/Avatar.js index 52bdbc1a50..8084f30f33 100644 --- a/src/Avatar.js +++ b/src/Avatar.js @@ -77,7 +77,9 @@ export function defaultAvatarUrlForString(s) { } const colorIndex = total % defaultColors.length; // overwritten color value in custom themes - const color = defaultColors[colorIndex]; + const cssVariable = `--avatar-background-colors_${colorIndex}`; + const cssValue = document.body.style.getPropertyValue(cssVariable); + const color = cssValue || defaultColors[colorIndex]; let dataUrl = colorToDataURLCache.get(color); if (!dataUrl) { dataUrl = urlForColor(color); From 76e04fbdfae00b8f29a29442ac1f1511a7cb6f7f Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 27 Apr 2020 19:39:07 +0200 Subject: [PATCH 5/7] update comment --- src/theme.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/theme.js b/src/theme.js index b7fbe40df4..ab5974f207 100644 --- a/src/theme.js +++ b/src/theme.js @@ -148,7 +148,7 @@ function setCustomThemeVars(customTheme) { function setCSSVariable(name, hexColor, doPct = true) { style.setProperty(`--${name}`, hexColor); if (doPct) { - // uses #rrggbbaa to define the color with alpha values at 0% and 50% + // uses #rrggbbaa to define the color with alpha values at 0%, 15% and 50% style.setProperty(`--${name}-0pct`, hexColor + "00"); style.setProperty(`--${name}-15pct`, hexColor + "26"); style.setProperty(`--${name}-50pct`, hexColor + "7F"); From 19fc6a93ec7004f6554bfffef82908512db88f1b Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 28 Apr 2020 10:41:35 +0200 Subject: [PATCH 6/7] fix tests --- src/Avatar.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Avatar.js b/src/Avatar.js index 8084f30f33..2e176e569e 100644 --- a/src/Avatar.js +++ b/src/Avatar.js @@ -59,6 +59,10 @@ function urlForColor(color) { canvas.width = size; canvas.height = size; const ctx = canvas.getContext("2d"); + // bail out when using jsdom in unit tests + if (!ctx) { + return ""; + } ctx.fillStyle = color; ctx.fillRect(0, 0, size, size); return canvas.toDataURL(); From 4978f8a2a9be4dc5e71fccda432f41d254dd27bf Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 28 Apr 2020 10:59:10 +0200 Subject: [PATCH 7/7] validate hex color --- src/Avatar.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Avatar.js b/src/Avatar.js index 2e176e569e..8393ce02b2 100644 --- a/src/Avatar.js +++ b/src/Avatar.js @@ -53,6 +53,13 @@ export function avatarUrlForUser(user, width, height, resizeMethod) { return url; } +function isValidHexColor(color) { + return typeof color === "string" && + (color.length === 7 || color.lengh === 9) && + color.charAt(0) === "#" && + !color.substr(1).split("").some(c => isNaN(parseInt(c, 16))); +} + function urlForColor(color) { const size = 40; const canvas = document.createElement("canvas"); @@ -86,8 +93,14 @@ export function defaultAvatarUrlForString(s) { const color = cssValue || defaultColors[colorIndex]; let dataUrl = colorToDataURLCache.get(color); if (!dataUrl) { - dataUrl = urlForColor(color); - colorToDataURLCache.set(color, dataUrl); + // validate color as this can come from account_data + // with custom theming + if (isValidHexColor(color)) { + dataUrl = urlForColor(color); + colorToDataURLCache.set(color, dataUrl); + } else { + dataUrl = ""; + } } return dataUrl; }