From 12430aebf1b513e022380eba8733198424c139fa Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Thu, 6 Jan 2022 02:54:12 +0100 Subject: [PATCH 01/10] Port theme functionality to TypeScript --- .../retrospring/features/settings/theme.ts | 100 ++++++++++++++++++ app/views/settings/_profile.haml | 16 +-- 2 files changed, 102 insertions(+), 14 deletions(-) create mode 100644 app/javascript/retrospring/features/settings/theme.ts diff --git a/app/javascript/retrospring/features/settings/theme.ts b/app/javascript/retrospring/features/settings/theme.ts new file mode 100644 index 00000000..c4cf02ff --- /dev/null +++ b/app/javascript/retrospring/features/settings/theme.ts @@ -0,0 +1,100 @@ +import "@melloware/coloris/dist/coloris.css"; +import Coloris from "@melloware/coloris"; + +let previewStyle = null; +let previewTimeout = null; + +const previewTheme = (): void => { + const payload = {}; + + Array.from(document.querySelectorAll('#update_theme .color')).forEach((color: HTMLInputElement) => { + const name = color.name.substring(6, color.name.length - 1); + payload[name] = parseInt(color.value.substr(1, 6), 16); + }); + + generateTheme(payload); +} + +const generateTheme = (payload: object): void => { + const themeAttributeMap = { + 'primary_color': 'primary', + 'primary_text': 'primary-text', + 'danger_color': 'danger', + 'danger_text': 'danger-text', + 'warning_color': 'warning', + 'warning_text': 'warning-text', + 'info_color': 'info', + 'info_text': 'info-text', + 'success_color': 'success', + 'success_text': 'success-text', + 'dark_color': 'dark', + 'dark_text': 'dark-text', + 'light_color': 'light', + 'light_text': 'light-text', + 'raised_background': 'raised-bg', + 'raised_accent': 'raised-accent', + 'background_color': 'background', + 'body_text': 'body-text', + 'input_color': 'input-bg', + 'input_text': 'input-text', + 'muted_text': 'muted-text' + }; + + let body = ":root {\n"; + + (Object.keys(payload)).forEach((payloadKey) => { + if (themeAttributeMap[payloadKey]) { + if (themeAttributeMap[payloadKey].includes('text')) { + const hex = getHexColorFromThemeValue(payload[payloadKey]); + body += `--${themeAttributeMap[payloadKey]}: ${getDecimalTripletsFromHex(hex)};\n`; + } + else { + body += `--${themeAttributeMap[payloadKey]}: #${getHexColorFromThemeValue(payload[payloadKey])};\n`; + } + } + }); + + body += "}"; + + previewStyle.innerHTML = body; +} + +const getHexColorFromThemeValue = (themeValue: string): string => { + return ('000000' + parseInt(themeValue).toString(16)).substr(-6, 6); +} + +const getDecimalTripletsFromHex = (hex: string): string => { + return hex.match(/.{1,2}/g).map((value) => parseInt(value, 16)).join(', '); +} + +export function themeDocumentHandler(): void { + if (!document.querySelector('#update_theme')) return; + + previewStyle = document.createElement('style'); + previewStyle.setAttribute('data-preview-style', ''); + document.body.appendChild(previewStyle); + + Coloris.init(); + + Array.from(document.querySelectorAll('#update_theme .color')).forEach((color: HTMLInputElement) => { + color.value = `#${getHexColorFromThemeValue(color.value)}`; + + Coloris({ + el: '.color', + wrap: false, + formatToggle: false, + alpha: false + }); + + color.addEventListener('input', () => { + clearTimeout(previewTimeout); + setTimeout(previewTheme, 1000); + }); + }); +} + +export function themeSubmitHandler(): void { + Array.from(document.querySelectorAll('#update_theme .color')).forEach((color: HTMLInputElement) => { + color.value = String(parseInt(color.value.substr(1, 6), 16)); + }); +} \ No newline at end of file diff --git a/app/views/settings/_profile.haml b/app/views/settings/_profile.haml index ea87af87..984588fa 100644 --- a/app/views/settings/_profile.haml +++ b/app/views/settings/_profile.haml @@ -8,16 +8,10 @@ .media-body = f.file_field :profile_picture, label: t('views.settings.profile.avatar'), accept: APP_CONFIG[:accepted_image_formats].join(',') - .row#profile-picture-crop-controls{ style: 'display: none;' } + .row.d-none#profile-picture-crop-controls .col-sm-10.col-md-8 %strong= t('views.settings.profile.avatar_adjust') %img#profile-picture-cropper{ src: current_user.profile_picture.url(:medium) } - .col-sm-2.col-md-4 - .btn-group - %button.btn.btn-inverse#cropper-zoom-out{ type: :button } - %i.fa.fa-search-minus - %button.btn.btn-inverse#cropper-zoom-in{ type: :button } - %i.fa.fa-search-plus .row.mb-2#profile-header-media .col @@ -25,16 +19,10 @@ .col-xs-12.mt-3.mt-sm-0.pl-3.pr-3 = f.file_field :profile_header, label: t('views.settings.profile.header'), accept: APP_CONFIG[:accepted_image_formats].join(',') - .row#profile-header-crop-controls{ style: 'display: none;' } + .row.d-none#profile-header-crop-controls .col-sm-10.col-md-8 %strong= t('views.settings.profile.header_adjust') %img#profile-header-cropper{ src: current_user.profile_header.url(:web) } - .col-sm-2.col-md-4 - .btn-group - %button.btn.btn-inverse#cropper-header-zoom-out{ type: :button } - %i.fa.fa-search-minus - %button.btn.btn-inverse#cropper-header-zoom-in{ type: :button } - %i.fa.fa-search-plus = f.check_box :show_foreign_themes, label: 'Render other user themes when visiting their profile' From 9b6abb9148bf1ecd90e0d61d9f7c6ed2ca3a5f05 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Thu, 6 Jan 2022 02:54:40 +0100 Subject: [PATCH 02/10] Port profile picture/header cropping functionality to TypeScript --- .../retrospring/features/settings/crop.ts | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 app/javascript/retrospring/features/settings/crop.ts diff --git a/app/javascript/retrospring/features/settings/crop.ts b/app/javascript/retrospring/features/settings/crop.ts new file mode 100644 index 00000000..e7e201d2 --- /dev/null +++ b/app/javascript/retrospring/features/settings/crop.ts @@ -0,0 +1,62 @@ +import 'croppr/dist/croppr.css'; +import Croppr from 'croppr'; + +const readImage = (file, callback) => callback((window.URL || window.webkitURL).createObjectURL(file)); + +export function profilePictureChangeHandler(event: Event) { + const input = event.target as HTMLInputElement; + + const cropControls = document.querySelector('#profile-picture-crop-controls'); + cropControls.classList.toggle('d-none'); + + if (input.files && input.files[0]) { + readImage(input.files[0], (src) => { + const updateValues = (data) => { + document.querySelector('#profile_picture_x').value = data.x; + document.querySelector('#profile_picture_y').value = data.y; + document.querySelector('#profile_picture_w').value = data.width; + document.querySelector('#profile_picture_h').value = data.height; + } + + const cropper = document.querySelector('#profile-picture-cropper'); + cropper.src = src; + + new Croppr(cropper, { + aspectRatio: 1, + startSize: [100, 100, '%'], + onCropStart: updateValues, + onCropMove: updateValues, + onCropEnd: updateValues + }); + }); + } +} + +export function profileHeaderChangeHandler(event: Event) { + const input = event.target as HTMLInputElement; + + const cropControls = document.querySelector('#profile-header-crop-controls'); + cropControls.classList.toggle('d-none'); + + if (input.files && input.files[0]) { + readImage(input.files[0], (src) => { + const updateValues = (data) => { + document.querySelector('#profile_header_x').value = data.x; + document.querySelector('#profile_header_y').value = data.y; + document.querySelector('#profile_header_w').value = data.width; + document.querySelector('#profile_header_h').value = data.height; + } + + const cropper = document.querySelector('#profile-header-cropper'); + cropper.src = src; + + new Croppr(cropper, { + aspectRatio: 7/30, + startSize: [100, 100, '%'], + onCropStart: updateValues, + onCropMove: updateValues, + onCropEnd: updateValues + }); + }); + } +} \ No newline at end of file From b18c68449e8abaa7bea3f7bb620b7334b5017bd0 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Thu, 6 Jan 2022 02:55:12 +0100 Subject: [PATCH 03/10] Port password confirmation functionality to TypeScript --- .../retrospring/features/settings/password.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 app/javascript/retrospring/features/settings/password.ts diff --git a/app/javascript/retrospring/features/settings/password.ts b/app/javascript/retrospring/features/settings/password.ts new file mode 100644 index 00000000..86be08c3 --- /dev/null +++ b/app/javascript/retrospring/features/settings/password.ts @@ -0,0 +1,14 @@ +/* +# see GitHub issue #11 +($ document).on "submit", "form#edit_user", (evt) -> + if ($ "input#user_current_password").val().length == 0 + evt.preventDefault() + $("button[data-target=#modal-passwd]").trigger 'click' +*/ + +export function userSubmitHandler(event: Event): void { + if (document.querySelector('#user_current_password').value.length === 0) { + event.preventDefault(); + document.querySelector('[data-target=#modal-passwd]').click(); + } +} \ No newline at end of file From e4485cb5cdeb997580f5b58bdde42079f717ec53 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Thu, 6 Jan 2022 02:55:46 +0100 Subject: [PATCH 04/10] Refactor mute rule functionality into seperate file --- .../retrospring/features/settings/index.ts | 27 +++++++++---------- .../retrospring/features/settings/mute.ts | 21 +++++++++++++-- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/app/javascript/retrospring/features/settings/index.ts b/app/javascript/retrospring/features/settings/index.ts index 138d756c..cbcc219c 100644 --- a/app/javascript/retrospring/features/settings/index.ts +++ b/app/javascript/retrospring/features/settings/index.ts @@ -1,18 +1,17 @@ -import {createDeleteEvent, createSubmitEvent} from "retrospring/features/settings/mute"; +import registerEvents from "utilities/registerEvents"; +import { muteDocumentHandler } from "./mute"; +import { profileHeaderChangeHandler, profilePictureChangeHandler } from "./crop"; +import { themeDocumentHandler, themeSubmitHandler } from "./theme"; +import { userSubmitHandler } from "./password"; export default (): void => { - const submit: HTMLButtonElement = document.getElementById('new-rule-submit') as HTMLButtonElement; - if (!submit || submit.classList.contains('js-initialized')) return; + muteDocumentHandler(); + themeDocumentHandler(); - const rulesList = document.querySelector('.js-rules-list'); - rulesList.querySelectorAll('.form-group:not(.js-initalized)').forEach(entry => { - const button = entry.querySelector('button') - button.onclick = createDeleteEvent(entry, button) - }); - const textEntry: HTMLButtonElement = document.getElementById('new-rule-text') as HTMLButtonElement; - const template: HTMLTemplateElement = document.getElementById('rule-template') as HTMLTemplateElement; - - submit.form.onsubmit = createSubmitEvent(submit, rulesList, textEntry, template) - - submit.classList.add('js-initialized') + registerEvents([ + { type: 'submit', target: document.querySelector('#update_theme'), handler: themeSubmitHandler }, + { type: 'submit', target: document.querySelector('#edit_user'), handler: userSubmitHandler }, + { type: 'change', target: document.querySelector('#user_profile_picture[type=file]'), handler: profilePictureChangeHandler }, + { type: 'change', target: document.querySelector('#user_profile_header[type=file]'), handler: profileHeaderChangeHandler } + ]); } \ No newline at end of file diff --git a/app/javascript/retrospring/features/settings/mute.ts b/app/javascript/retrospring/features/settings/mute.ts index a402538d..961cee0c 100644 --- a/app/javascript/retrospring/features/settings/mute.ts +++ b/app/javascript/retrospring/features/settings/mute.ts @@ -1,6 +1,6 @@ import Rails from '@rails/ujs'; -export function createSubmitEvent( +function createSubmitEvent( submit: HTMLButtonElement, rulesList: HTMLDivElement, textEntry: HTMLButtonElement, @@ -36,7 +36,7 @@ export function createSubmitEvent( }; } -export function createDeleteEvent( +function createDeleteEvent( entry: HTMLDivElement, deleteButton: HTMLButtonElement ): () => void { @@ -56,4 +56,21 @@ export function createDeleteEvent( } }) } +} + +export function muteDocumentHandler() { + const submit: HTMLButtonElement = document.getElementById('new-rule-submit') as HTMLButtonElement; + if (!submit || submit.classList.contains('js-initialized')) return; + + const rulesList = document.querySelector('.js-rules-list'); + rulesList.querySelectorAll('.form-group:not(.js-initalized)').forEach(entry => { + const button = entry.querySelector('button') + button.onclick = createDeleteEvent(entry, button) + }); + const textEntry: HTMLButtonElement = document.getElementById('new-rule-text') as HTMLButtonElement; + const template: HTMLTemplateElement = document.getElementById('rule-template') as HTMLTemplateElement; + + submit.form.onsubmit = createSubmitEvent(submit, rulesList, textEntry, template) + + submit.classList.add('js-initialized'); } \ No newline at end of file From ed9f0f0123c67ba93472ab1e3c92fd69760f98fa Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Thu, 6 Jan 2022 02:56:00 +0100 Subject: [PATCH 05/10] Remove legacy settings functionality --- app/javascript/legacy/settings.coffee | 200 -------------------------- app/javascript/packs/legacy.coffee | 4 - 2 files changed, 204 deletions(-) delete mode 100644 app/javascript/legacy/settings.coffee diff --git a/app/javascript/legacy/settings.coffee b/app/javascript/legacy/settings.coffee deleted file mode 100644 index 4b6575d8..00000000 --- a/app/javascript/legacy/settings.coffee +++ /dev/null @@ -1,200 +0,0 @@ -# see GitHub issue #11 -($ document).on "submit", "form#edit_user", (evt) -> - if ($ "input#user_current_password").val().length == 0 - evt.preventDefault() - $("button[data-target=#modal-passwd]").trigger 'click' - -readImage = (file, callback) -> - fr = new FileReader() - fr.addEventListener "load", (e) -> - callback e.target.result - fr.readAsBinaryString file - -freeURL = () -> - -if window.URL? or window.webkitURL? - readImage = (file, callback) -> - callback (window.URL || window.webkitURL).createObjectURL file - freeURL = (url) -> - URL.revokeObjectURL url - - -# Profile pic -($ document).on 'change', 'input#user_profile_picture[type=file]', -> - input = this - - ($ '#profile-picture-crop-controls').slideUp 400, -> - if input.files and input.files[0] - readImage input.files[0], (src) -> - cropper = ($ '#profile-picture-cropper') - preview = ($ '#profile-picture-preview') - - updateVars = (data, action) -> - ($ '#profile_picture_x').val Math.floor(data.x / data.scale) - ($ '#profile_picture_y').val Math.floor(data.y / data.scale) - ($ '#profile_picture_w').val Math.floor(data.w / data.scale) - ($ '#profile_picture_h').val Math.floor(data.h / data.scale) -# rx = 100 / data.w -# ry = 100 / data.h -# ($ '#profile-picture-preview').css -# width: Math.round(rx * preview[0].naturalWidth) + 'px' -# height: Math.round(ry * preview[0].naturalHeight) + 'px' -# marginLeft: '-' + Math.round(rx * data.x) + 'px' -# marginTop: '-' + Math.round(ry * data.y) + 'px' - - cropper.on 'load', -> - if ({}.toString).call(src) == "[object URL]" - freeURL src - - side = if cropper[0].naturalWidth > cropper[0].naturalHeight - cropper[0].naturalHeight - else - cropper[0].naturalWidth - - cropper.guillotine - width: side - height: side - onChange: updateVars - - updateVars cropper.guillotine('getData'), 'drag' # just because - - unless ($ '#profile-picture-crop-controls')[0].dataset.bound? - ($ '#cropper-zoom-out').click -> cropper.guillotine 'zoomOut' - ($ '#cropper-zoom-in').click -> cropper.guillotine 'zoomIn' - ($ '#profile-picture-crop-controls')[0].dataset.bound = true - ($ '#profile-picture-crop-controls').slideDown() - - cropper.attr 'src', src - -($ document).on 'change', 'input#user_profile_header[type=file]', -> - input = this - - ($ '#profile-header-crop-controls').slideUp 400, -> - if input.files and input.files[0] - readImage input.files[0], (src) -> - cropper = ($ '#profile-header-cropper') - preview = ($ '#profile-header-preview') - - updateVars = (data, action) -> - ($ '#profile_header_x').val Math.floor(data.x / data.scale) - ($ '#profile_header_y').val Math.floor(data.y / data.scale) - ($ '#profile_header_w').val Math.floor(data.w / data.scale) - ($ '#profile_header_h').val Math.floor(data.h / data.scale) - - cropper.on 'load', -> - if ({}.toString).call(src) == "[object URL]" - freeURL src - - cropper.guillotine - width: 1500 - height: 350 - onChange: updateVars - - updateVars cropper.guillotine('getData'), 'drag' - - unless ($ '#profile-header-crop-controls')[0].dataset.bound? - ($ '#cropper-header-zoom-out').click -> cropper.guillotine 'zoomOut' - ($ '#cropper-header-zoom-in').click -> cropper.guillotine 'zoomIn' - ($ '#profile-header-crop-controls')[0].dataset.bound = true - ($ '#profile-header-crop-controls').slideDown() - - cropper.attr 'src', src - -# theming - -previewStyle = null - -$(document).on 'ready turbolinks:load', -> - if $('#update_theme').length > 0 - previewStyle = document.createElement 'style' - document.body.appendChild previewStyle - - previewTimeout = null - - $('#update_theme .color').each -> - $this = $ this - this.value = '#' + getHexColorFromThemeValue(this.value) - - $this.minicolors - control: 'hue' - defaultValue: this.value - letterCase: 'lowercase' - position: 'bottom left' - theme: 'bootstrap' - inline: false - change: -> - clearTimeout previewTimeout - previewTimeout = setTimeout(previewTheme, 1000) - -$(document).on 'click', 'a.theme_preset', (event) -> - preset = [].concat themePresets[this.dataset.preset] - $('#update_theme .color').each -> - $(this).minicolors 'value', '#' + getHexColorFromThemeValue(preset.shift()) - -previewTheme = -> - payload = {} - - $('#update_theme').find('.color').each -> - n = this.name.substr 6, this.name.length - 7 - payload[n] = parseInt this.value.substr(1, 6), 16 - - generateTheme payload - - null - -generateTheme = (payload) -> - theme_attribute_map = { - 'primary_color': 'primary', - 'primary_text': 'primary-text', - 'danger_color': 'danger', - 'danger_text': 'danger-text', - 'warning_color': 'warning', - 'warning_text': 'warning-text', - 'info_color': 'info', - 'info_text': 'info-text', - 'success_color': 'success', - 'success_text': 'success-text', - 'dark_color': 'dark', - 'dark_text': 'dark-text', - 'light_color': 'light', - 'light_text': 'light-text', - 'raised_background': 'raised-bg', - 'raised_accent': 'raised-accent', - 'background_color': 'background', - 'body_text': 'body-text', - 'input_color': 'input-bg', - 'input_text': 'input-text', - 'muted_text': 'muted-text' - } - - body = ":root {\n" - - (Object.keys(payload)).forEach (plKey) -> - if theme_attribute_map[plKey] - if theme_attribute_map[plKey].includes 'text' - hex = getHexColorFromThemeValue(payload[plKey]) - body += "--#{theme_attribute_map[plKey]}: #{getDecimalTripletsFromHex(hex)};\n" - else - body += "--#{theme_attribute_map[plKey]}: ##{getHexColorFromThemeValue(payload[plKey])};\n" - - body += "}" - - previewStyle.innerHTML = body - -getHexColorFromThemeValue = (themeValue) -> - return ('000000' + parseInt(themeValue).toString(16)).substr(-6, 6) - -getDecimalTripletsFromHex = (hex) -> - return hex.match(/.{1,2}/g).map((value) -> parseInt(value, 16)).join(', ') - -themePresets = { - rs: [0x5E35B1, 0xFFFFFF, 0xFF0039, 0xFFFFFF, 0x3FB618, 0xFFFFFF, 0xFF7518, 0xFFFFFF, 0x9954BB, 0xFFFFFF, 0x222222, 0xEEEEEE, 0xF9F9F9, 0x151515, 0x5E35B1, 0xFFFFFF, 0x222222, 0xbbbbbb, 0xFFFFFF, 0x000000, 0x5E35B1], - dc: [0x141414, 0xeeeeee, 0x362222, 0xeeeeee, 0x1d2e1d, 0xeeeeee, 0x404040, 0xeeeeee, 0xb8b8b8, 0x3b3b3b, 0x303030, 0xEEEEEE, 0x202020, 0xeeeeee, 0x9c9a9a, 0x363636, 0xe6e6e6, 0xbbbbbb, 0x383838, 0xebebeb, 0x787676], - lc: [0xebebeb, 0x111111, 0xf76363, 0x111111, 0x8aff94, 0x111111, 0xffbd7f, 0x111111, 0x474747, 0xc4c4c4, 0xcfcfcf, 0x111111, 0xdfdfdf, 0x111111, 0x636565, 0xc9c9c9, 0x191919, 0x444444, 0xc7c7c7, 0x141414, 0x878989] -} - -$(document).on 'submit', '#update_theme', (event) -> - $this = $ this - $this.find('.color').each -> - this.value = parseInt this.value.substr(1, 6), 16 - true diff --git a/app/javascript/packs/legacy.coffee b/app/javascript/packs/legacy.coffee index 83d3cb6a..abea5bd7 100644 --- a/app/javascript/packs/legacy.coffee +++ b/app/javascript/packs/legacy.coffee @@ -8,16 +8,13 @@ import 'bootstrap' import 'jquery.guillotine' import 'particleground/jquery.particleground.min' import 'jquery.growl' -import 'jquery-minicolors' import 'sweetalert' import Cookies from 'js-cookie' import moment from 'moment' require('nprogress/nprogress.css') require('jquery.growl/stylesheets/jquery.growl.css') -require('jquery.guillotine/css/jquery.guillotine.css') require('sweetalert/dist/sweetalert.css') -require('jquery-minicolors/jquery.minicolors.css') # this file is generated by Rails import I18n from '../legacy/i18n' @@ -26,7 +23,6 @@ import '../legacy/answerbox' import '../legacy/memes' import '../legacy/notifications' import '../legacy/pagination' -import '../legacy/settings' import '../legacy/report' import '../legacy/locale-box' import '../legacy/util' From 2796d720722717782e1b180bca104b333a2aa261 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Thu, 6 Jan 2022 02:56:35 +0100 Subject: [PATCH 06/10] Remove dropped dependencies --- package.json | 4 ++-- tsconfig.json | 1 + yarn.lock | 26 +++++++++++--------------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index abc3f656..ce4b5854 100644 --- a/package.json +++ b/package.json @@ -5,16 +5,16 @@ }, "dependencies": { "@babel/preset-typescript": "^7.12.7", + "@melloware/coloris": "^0.10.0", "@rails/ujs": "^6.1.0", "bootstrap": "^4.5.3", "cheet.js": "^0.3.3", "core-js": "^3.8.1", + "croppr": "^2.3.1", "i18n-js": "^3.8.0", "jquery": "^3.5.1", - "jquery-minicolors": "^2.1.10", "jquery-ujs": "^1.2.2", "jquery.growl": "^1.3.5", - "jquery.guillotine": "^1.4.3", "jquery.turbolinks": "^2.1.0", "js-cookie": "2.2.1", "nprogress": "^0.2.0", diff --git a/tsconfig.json b/tsconfig.json index fe957b80..6ec41c38 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,6 +3,7 @@ "declaration": false, "emitDecoratorMetadata": true, "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, "lib": ["es6", "dom"], "module": "es6", "moduleResolution": "node", diff --git a/yarn.lock b/yarn.lock index 24fa010f..6ca4fa11 100644 --- a/yarn.lock +++ b/yarn.lock @@ -869,6 +869,11 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@melloware/coloris@^0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@melloware/coloris/-/coloris-0.10.0.tgz#13483d2b35a78d52c8b802c444c85f3c0c1f6f05" + integrity sha512-AsHgSIZJKjdELApdcX05WA+Nul4zyeN72YyJtNMuVegPHszafkJKdaj+vmWYZBE4f8naKqwaNY9BuU6v1o0ivQ== + "@nodelib/fs.scandir@2.1.3": version "2.1.3" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" @@ -2370,6 +2375,11 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +croppr@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/croppr/-/croppr-2.3.1.tgz#d279e006531240fa8ebf2681e4127ae7c42b074e" + integrity sha512-0rvTl4VmR3I4AahjJPF1u9IlT7ckvjIcgaLnUjYaY+UZsP9oxlVYZWYDuqM3SVCQiaI7DXMjR7wOEYT+mydOFg== + cross-spawn@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" @@ -4485,13 +4495,6 @@ jest-worker@^26.5.0: merge-stream "^2.0.0" supports-color "^7.0.0" -jquery-minicolors@^2.1.10: - version "2.1.10" - resolved "https://registry.yarnpkg.com/jquery-minicolors/-/jquery-minicolors-2.1.10.tgz#b29eea541d9a32b4e26923168fb2c16271867379" - integrity sha1-sp7qVB2aMrTiaSMWj7LBYnGGc3k= - dependencies: - jquery ">= 1.7.x" - jquery-ujs@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/jquery-ujs/-/jquery-ujs-1.2.2.tgz#6a8ef1020e6b6dda385b90a4bddc128c21c56397" @@ -4504,19 +4507,12 @@ jquery.growl@^1.3.5: resolved "https://registry.yarnpkg.com/jquery.growl/-/jquery.growl-1.3.5.tgz#fa1c4d758e0d7686551e15896b1aaac2bb1af7f1" integrity sha1-+hxNdY4NdoZVHhWJaxqqwrsa9/E= -jquery.guillotine@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/jquery.guillotine/-/jquery.guillotine-1.4.3.tgz#c2463c367feaa8b229e02e2fbbae2e29cfd07225" - integrity sha1-wkY8Nn/qqLIp4C4vu64uKc/QciU= - dependencies: - jquery ">= 1.8.0" - jquery.turbolinks@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/jquery.turbolinks/-/jquery.turbolinks-2.1.0.tgz#36036bb997c48d53bceb345521fbdf7da887a62c" integrity sha1-NgNruZfEjVO86zRVIfvffaiHpiw= -"jquery@>= 1.7.x", "jquery@>= 1.8.0", jquery@>=1.8.0, jquery@^3.0, jquery@^3.5.1: +jquery@>=1.8.0, jquery@^3.0, jquery@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz#d7b4d08e1bfdb86ad2f1a3d039ea17304717abb5" integrity sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg== From fb9d8bf9472ee2b6665179d8548de74e7da769c4 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Thu, 6 Jan 2022 03:04:01 +0100 Subject: [PATCH 07/10] Fix TSLint nits --- app/javascript/retrospring/features/settings/crop.ts | 4 ++-- app/javascript/retrospring/features/settings/mute.ts | 2 +- app/javascript/retrospring/features/settings/theme.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/javascript/retrospring/features/settings/crop.ts b/app/javascript/retrospring/features/settings/crop.ts index e7e201d2..13e09c77 100644 --- a/app/javascript/retrospring/features/settings/crop.ts +++ b/app/javascript/retrospring/features/settings/crop.ts @@ -3,7 +3,7 @@ import Croppr from 'croppr'; const readImage = (file, callback) => callback((window.URL || window.webkitURL).createObjectURL(file)); -export function profilePictureChangeHandler(event: Event) { +export function profilePictureChangeHandler(event: Event): void { const input = event.target as HTMLInputElement; const cropControls = document.querySelector('#profile-picture-crop-controls'); @@ -32,7 +32,7 @@ export function profilePictureChangeHandler(event: Event) { } } -export function profileHeaderChangeHandler(event: Event) { +export function profileHeaderChangeHandler(event: Event): void { const input = event.target as HTMLInputElement; const cropControls = document.querySelector('#profile-header-crop-controls'); diff --git a/app/javascript/retrospring/features/settings/mute.ts b/app/javascript/retrospring/features/settings/mute.ts index 961cee0c..e8580f8c 100644 --- a/app/javascript/retrospring/features/settings/mute.ts +++ b/app/javascript/retrospring/features/settings/mute.ts @@ -58,7 +58,7 @@ function createDeleteEvent( } } -export function muteDocumentHandler() { +export function muteDocumentHandler(): void { const submit: HTMLButtonElement = document.getElementById('new-rule-submit') as HTMLButtonElement; if (!submit || submit.classList.contains('js-initialized')) return; diff --git a/app/javascript/retrospring/features/settings/theme.ts b/app/javascript/retrospring/features/settings/theme.ts index c4cf02ff..d7d340c8 100644 --- a/app/javascript/retrospring/features/settings/theme.ts +++ b/app/javascript/retrospring/features/settings/theme.ts @@ -15,7 +15,7 @@ const previewTheme = (): void => { generateTheme(payload); } -const generateTheme = (payload: object): void => { +const generateTheme = (payload: Record): void => { const themeAttributeMap = { 'primary_color': 'primary', 'primary_text': 'primary-text', @@ -88,7 +88,7 @@ export function themeDocumentHandler(): void { color.addEventListener('input', () => { clearTimeout(previewTimeout); - setTimeout(previewTheme, 1000); + previewTimeout = setTimeout(previewTheme, 1000); }); }); } From 6dcb5a2afbb7a0d37951d7e8273bc5da125aa899 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Thu, 6 Jan 2022 03:14:40 +0100 Subject: [PATCH 08/10] Remove `jquery.guillotine` import in legacy CoffeeScript --- app/javascript/packs/legacy.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/app/javascript/packs/legacy.coffee b/app/javascript/packs/legacy.coffee index abea5bd7..bd979b9f 100644 --- a/app/javascript/packs/legacy.coffee +++ b/app/javascript/packs/legacy.coffee @@ -5,7 +5,6 @@ import '../legacy/jquery' import {} from 'jquery-ujs' import 'popper.js' import 'bootstrap' -import 'jquery.guillotine' import 'particleground/jquery.particleground.min' import 'jquery.growl' import 'sweetalert' From f035cc2f895664c0d450fa2e7c6cc01433388ee9 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Thu, 6 Jan 2022 13:02:18 +0100 Subject: [PATCH 09/10] Remove reference code comment --- app/javascript/retrospring/features/settings/password.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/javascript/retrospring/features/settings/password.ts b/app/javascript/retrospring/features/settings/password.ts index 86be08c3..83d53265 100644 --- a/app/javascript/retrospring/features/settings/password.ts +++ b/app/javascript/retrospring/features/settings/password.ts @@ -1,11 +1,3 @@ -/* -# see GitHub issue #11 -($ document).on "submit", "form#edit_user", (evt) -> - if ($ "input#user_current_password").val().length == 0 - evt.preventDefault() - $("button[data-target=#modal-passwd]").trigger 'click' -*/ - export function userSubmitHandler(event: Event): void { if (document.querySelector('#user_current_password').value.length === 0) { event.preventDefault(); From 0a8ff20f361657de66594e3c67460259f993abc8 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Fri, 7 Jan 2022 14:12:45 +0100 Subject: [PATCH 10/10] Fix theme page breaking after a reload --- .../retrospring/features/settings/theme.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/javascript/retrospring/features/settings/theme.ts b/app/javascript/retrospring/features/settings/theme.ts index d7d340c8..0f30b7cd 100644 --- a/app/javascript/retrospring/features/settings/theme.ts +++ b/app/javascript/retrospring/features/settings/theme.ts @@ -69,6 +69,7 @@ const getDecimalTripletsFromHex = (hex: string): string => { export function themeDocumentHandler(): void { if (!document.querySelector('#update_theme')) return; + if (document.querySelector('#clr-picker')) return; previewStyle = document.createElement('style'); previewStyle.setAttribute('data-preview-style', ''); @@ -77,7 +78,21 @@ export function themeDocumentHandler(): void { Coloris.init(); Array.from(document.querySelectorAll('#update_theme .color')).forEach((color: HTMLInputElement) => { - color.value = `#${getHexColorFromThemeValue(color.value)}`; + // If there already is a hex-color in the input, skip + if (color.value.startsWith('#')) return; + + let colorValue; + + // match for value="[digits]" to ALWAYS get a color value + // TODO: Fix this later with rethinking the entire lifecycle, or dropping Turbolinks + colorValue = color.outerHTML.match(/value="(\d+)"/)[1]; + + // matching failed, or no result was found, we just fallback to the input value + if (colorValue === null) { + colorValue = color.value; + } + + color.value = `#${getHexColorFromThemeValue(colorValue)}`; Coloris({ el: '.color',