diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index f8ea94911..0adccae7a 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -130,7 +130,8 @@ ENV \ SAML_PUBLIC_CERTFILE="" \ SAML_IDENTIFIER_FORMAT="" \ SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE="" \ - SAML_ATTRIBUTES="" + SAML_ATTRIBUTES="" \ + DEFAULT_WAIT_SPINNER="" # Install OS RUN set -o xtrace \ diff --git a/Dockerfile b/Dockerfile index 6ba008bad..75ae107d4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -137,7 +137,8 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build- SAML_IDENTIFIER_FORMAT="" \ SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE="" \ SAML_ATTRIBUTES="" \ - ORACLE_OIM_ENABLED=false + ORACLE_OIM_ENABLED=false \ + DEFAULT_WAIT_SPINNER="" # Copy the app to the image COPY ${SRC_PATH} /home/wekan/app diff --git a/client/components/lists/listBody.jade b/client/components/lists/listBody.jade index ce671cdef..b286347a9 100644 --- a/client/components/lists/listBody.jade +++ b/client/components/lists/listBody.jade @@ -23,14 +23,10 @@ template(name="listBody") i.fa.fa-plus template(name="spinnerList") - .sk-spinner.sk-spinner-wave.sk-spinner-list( - class=currentBoard.colorClass + .sk-spinner.sk-spinner-list( + class="{{currentBoard.colorClass}} {{getSkSpinnerName}}" id="showMoreResults") - .sk-rect1 - .sk-rect2 - .sk-rect3 - .sk-rect4 - .sk-rect5 + +spinnerRaw template(name="addCardForm") .minicard.minicard-composer.js-composer diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 65eff9ed5..6073f3900 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -1,3 +1,5 @@ +import { Spinner } from '/client/lib/spinner'; + const subManager = new SubsManager(); const InfiniteScrollIter = 10; @@ -696,7 +698,7 @@ BlazeComponent.extendComponent({ }, }).register('searchElementPopup'); -BlazeComponent.extendComponent({ +(class extends Spinner { onCreated() { this.cardlimit = this.parentComponent().cardlimit; @@ -724,7 +726,7 @@ BlazeComponent.extendComponent({ .parentComponent() .data()._id; } - }, + } onRendered() { this.spinner = this.find('.sk-spinner-list'); @@ -739,47 +741,58 @@ BlazeComponent.extendComponent({ ); this.updateList(); - }, + } onDestroyed() { $(this.container).off(`scroll.spinner_${this.swimlaneId}_${this.listId}`); $(window).off(`resize.spinner_${this.swimlaneId}_${this.listId}`); - }, + } + + checkIdleTime() { + return window.requestIdleCallback || + function(handler) { + const startTime = Date.now(); + return setTimeout(function() { + handler({ + didTimeout: false, + timeRemaining() { + return Math.max(0, 50.0 - (Date.now() - startTime)); + }, + }); + }, 1); + }; + } updateList() { // Use fallback when requestIdleCallback is not available on iOS and Safari // https://www.afasterweb.com/2017/11/20/utilizing-idle-moments/ - checkIdleTime = - window.requestIdleCallback || - function(handler) { - const startTime = Date.now(); - return setTimeout(function() { - handler({ - didTimeout: false, - timeRemaining() { - return Math.max(0, 50.0 - (Date.now() - startTime)); - }, - }); - }, 1); - }; if (this.spinnerInView()) { this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter); - checkIdleTime(() => this.updateList()); + this.checkIdleTime(() => this.updateList()); } - }, + } spinnerInView() { - const parentViewHeight = this.container.clientHeight; - const bottomViewPosition = this.container.scrollTop + parentViewHeight; - - const threshold = this.spinner.offsetTop; - // spinner deleted if (!this.spinner.offsetTop) { return false; } - return bottomViewPosition > threshold; - }, -}).register('spinnerList'); + const parentViewHeight = this.container.clientHeight; + const bottomViewPosition = this.container.scrollTop + parentViewHeight; + + let spinnerOffsetTop = this.spinner.offsetTop; + + const addCard = $(this.container).find("a.open-minicard-composer").first()[0]; + if (addCard !== undefined) { + spinnerOffsetTop -= addCard.clientHeight; + } + + return bottomViewPosition > spinnerOffsetTop; + } + + getSkSpinnerName() { + return "sk-spinner-" + super.getSpinnerName().toLowerCase(); + } +}.register('spinnerList')); diff --git a/client/components/main/spinner.jade b/client/components/main/spinner.jade new file mode 100644 index 000000000..1b4d6dad0 --- /dev/null +++ b/client/components/main/spinner.jade @@ -0,0 +1,5 @@ +template(name="spinner") + +Template.dynamic(template=getSpinnerTemplate) + +template(name="spinnerRaw") + +Template.dynamic(template=getSpinnerTemplateRaw) diff --git a/client/components/main/spinner.js b/client/components/main/spinner.js new file mode 100644 index 000000000..2d7ab35f7 --- /dev/null +++ b/client/components/main/spinner.js @@ -0,0 +1,11 @@ +import { Spinner } from '/client/lib/spinner'; + +(class extends Spinner { +}.register('spinner')); + +(class extends Spinner { + getSpinnerTemplateRaw() { + let ret = super.getSpinnerTemplate() + 'Raw'; + return ret; + } +}.register('spinnerRaw')); diff --git a/client/components/main/spinner.tpl.jade b/client/components/main/spinner.tpl.jade deleted file mode 100644 index 9310a6e5b..000000000 --- a/client/components/main/spinner.tpl.jade +++ /dev/null @@ -1,6 +0,0 @@ -.sk-spinner.sk-spinner-wave(class=currentBoard.colorClass) - .sk-rect1 - .sk-rect2 - .sk-rect3 - .sk-rect4 - .sk-rect5 diff --git a/client/components/main/spinner_bounce.jade b/client/components/main/spinner_bounce.jade new file mode 100644 index 000000000..5eda2295a --- /dev/null +++ b/client/components/main/spinner_bounce.jade @@ -0,0 +1,11 @@ +template(name="spinnerBounce") + .sk-spinner.sk-spinner-bounce(class=currentBoard.colorClass) + +spinnerBounceRaw + +template(name="spinnerBounceRaw") + .sk-bounce1 + |   + .sk-bounce2 + |   + .sk-bounce3 + |   diff --git a/client/components/main/spinner_bounce.styl b/client/components/main/spinner_bounce.styl new file mode 100644 index 000000000..7d9ab0248 --- /dev/null +++ b/client/components/main/spinner_bounce.styl @@ -0,0 +1,44 @@ +@import 'nib' + +// From https://github.com/tobiasahlin/SpinKit +.sk-spinner-bounce { + margin: 100px auto 0; + width: 70px; + text-align: center; + + div { + width: 18px; + height: 18px; + background-color: #333; + + border-radius: 100%; + display: inline-block; + -webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both; + animation: sk-bouncedelay 1.4s infinite ease-in-out both; + } + + .sk-bounce1 { + -webkit-animation-delay: -0.32s; + animation-delay: -0.32s; + } + + .sk-bounce2 { + -webkit-animation-delay: -0.16s; + animation-delay: -0.16s; + } +} + +@-webkit-keyframes sk-bouncedelay { + 0%, 80%, 100% { -webkit-transform: scale(0) } + 40% { -webkit-transform: scale(1.0) } +} + +@keyframes sk-bouncedelay { + 0%, 80%, 100% { + -webkit-transform: scale(0); + transform: scale(0); + } 40% { + -webkit-transform: scale(1.0); + transform: scale(1.0); + } +} diff --git a/client/components/main/spinner_cube.jade b/client/components/main/spinner_cube.jade new file mode 100644 index 000000000..696d2f5ad --- /dev/null +++ b/client/components/main/spinner_cube.jade @@ -0,0 +1,8 @@ +template(name="spinnerCube") + .sk-spinner.sk-spinner-cube(class=currentBoard.colorClass) + +spinnerCubeRaw + +template(name="spinnerCubeRaw") + .sk-cube1 + .sk-cube2 + .sk-cube3 diff --git a/client/components/main/spinner_cube.styl b/client/components/main/spinner_cube.styl new file mode 100644 index 000000000..92e6e2a11 --- /dev/null +++ b/client/components/main/spinner_cube.styl @@ -0,0 +1,52 @@ +@import 'nib' + +// From https://github.com/tobiasahlin/SpinKit +.sk-spinner-cube { + margin: 100px auto; + width: 40px; + height: 40px; + position: relative; +} + +.sk-cube1, .sk-cube2 { + background-color: #333; + width: 15px; + height: 15px; + position: absolute; + top: 0; + left: 0; + + -webkit-animation: sk-cubemove 1.8s infinite ease-in-out; + animation: sk-cubemove 1.8s infinite ease-in-out; +} + +.sk-cube2 { + -webkit-animation-delay: -0.9s; + animation-delay: -0.9s; +} + +@-webkit-keyframes sk-cubemove { + 25% { -webkit-transform: translateX(35px) rotate(-90deg) scale(0.5) } + 50% { -webkit-transform: translateX(35px) translateY(35px) rotate(-180deg) } + 75% { -webkit-transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5) } + 100% { -webkit-transform: rotate(-360deg) } +} + +@keyframes sk-cubemove { + 25% { + transform: translateX(35px) rotate(-90deg) scale(0.5); + -webkit-transform: translateX(35px) rotate(-90deg) scale(0.5); + } 50% { + transform: translateX(35px) translateY(35px) rotate(-179deg); + -webkit-transform: translateX(35px) translateY(35px) rotate(-179deg); + } 50.1% { + transform: translateX(35px) translateY(35px) rotate(-180deg); + -webkit-transform: translateX(35px) translateY(35px) rotate(-180deg); + } 75% { + transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5); + -webkit-transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5); + } 100% { + transform: rotate(-360deg); + -webkit-transform: rotate(-360deg); + } +} diff --git a/client/components/main/spinner_cube_grid.jade b/client/components/main/spinner_cube_grid.jade new file mode 100644 index 000000000..55faf11af --- /dev/null +++ b/client/components/main/spinner_cube_grid.jade @@ -0,0 +1,14 @@ +template(name="spinnerCubeGrid") + .sk-spinner.sk-spinner-cube-grid(class=currentBoard.colorClass) + +spinnerCubeGridRaw + +template(name="spinnerCubeGridRaw") + .sk-cube-grid.sk-cube-grid1 + .sk-cube-grid.sk-cube-grid2 + .sk-cube-grid.sk-cube-grid3 + .sk-cube-grid.sk-cube-grid4 + .sk-cube-grid.sk-cube-grid5 + .sk-cube-grid.sk-cube-grid6 + .sk-cube-grid.sk-cube-grid7 + .sk-cube-grid.sk-cube-grid8 + .sk-cube-grid.sk-cube-grid9 diff --git a/client/components/main/spinner_cube_grid.styl b/client/components/main/spinner_cube_grid.styl new file mode 100644 index 000000000..042aa10fe --- /dev/null +++ b/client/components/main/spinner_cube_grid.styl @@ -0,0 +1,64 @@ +@import 'nib' + +// From https://github.com/tobiasahlin/SpinKit +.sk-spinner-cube-grid { + width: 40px; + height: 40px; + margin: 100px auto; +} + +.sk-spinner-cube-grid .sk-cube-grid { + width: 33%; + height: 33%; + background-color: #333; + float: left; + -webkit-animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out; + animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out; +} +.sk-spinner-cube-grid .sk-cube-grid1 { + -webkit-animation-delay: 0.2s; + animation-delay: 0.2s; } +.sk-spinner-cube-grid .sk-cube-grid2 { + -webkit-animation-delay: 0.3s; + animation-delay: 0.3s; } +.sk-spinner-cube-grid .sk-cube-grid3 { + -webkit-animation-delay: 0.4s; + animation-delay: 0.4s; } +.sk-spinner-cube-grid .sk-cube-grid4 { + -webkit-animation-delay: 0.1s; + animation-delay: 0.1s; } +.sk-spinner-cube-grid .sk-cube-grid5 { + -webkit-animation-delay: 0.2s; + animation-delay: 0.2s; } +.sk-spinner-cube-grid .sk-cube-grid6 { + -webkit-animation-delay: 0.3s; + animation-delay: 0.3s; } +.sk-spinner-cube-grid .sk-cube-grid7 { + -webkit-animation-delay: 0s; + animation-delay: 0s; } +.sk-spinner-cube-grid .sk-cube-grid8 { + -webkit-animation-delay: 0.1s; + animation-delay: 0.1s; } +.sk-spinner-cube-grid .sk-cube-grid9 { + -webkit-animation-delay: 0.2s; + animation-delay: 0.2s; } + +@-webkit-keyframes sk-cubeGridScaleDelay { + 0%, 70%, 100% { + -webkit-transform: scale3D(1, 1, 1); + transform: scale3D(1, 1, 1); + } 35% { + -webkit-transform: scale3D(0, 0, 1); + transform: scale3D(0, 0, 1); + } +} + +@keyframes sk-cubeGridScaleDelay { + 0%, 70%, 100% { + -webkit-transform: scale3D(1, 1, 1); + transform: scale3D(1, 1, 1); + } 35% { + -webkit-transform: scale3D(0, 0, 1); + transform: scale3D(0, 0, 1); + } +} diff --git a/client/components/main/spinner_dot.jade b/client/components/main/spinner_dot.jade new file mode 100644 index 000000000..187511116 --- /dev/null +++ b/client/components/main/spinner_dot.jade @@ -0,0 +1,7 @@ +template(name="spinnerDot") + .sk-spinner.sk-spinner-dot(class=currentBoard.colorClass) + +spinnerDotRaw + +template(name="spinnerDotRaw") + .sk-dot1 + .sk-dot2 diff --git a/client/components/main/spinner_dot.styl b/client/components/main/spinner_dot.styl new file mode 100644 index 000000000..5f2e36da4 --- /dev/null +++ b/client/components/main/spinner_dot.styl @@ -0,0 +1,51 @@ +@import 'nib' + +// From https://github.com/tobiasahlin/SpinKit +.sk-spinner-dot { + margin: 100px auto; + width: 40px; + height: 40px; + position: relative; + text-align: center; + + -webkit-animation: sk-rotate 2.0s infinite linear; + animation: sk-rotate 2.0s infinite linear; +} + +.sk-dot1, .sk-dot2 { + width: 40%; + height: 40%; + display: inline-block; + position: absolute; + top: 0; + background-color: #333; + border-radius: 100%; + + -webkit-animation: sk-bounce 2.0s infinite ease-in-out; + animation: sk-bounce 2.0s infinite ease-in-out; +} + +.sk-dot2 { + top: auto; + bottom: 0; + -webkit-animation-delay: -1.0s; + animation-delay: -1.0s; +} + +@-webkit-keyframes sk-rotate { 100% { -webkit-transform: rotate(360deg) }} +@keyframes sk-rotate { 100% { transform: rotate(360deg); -webkit-transform: rotate(360deg) }} + +@-webkit-keyframes sk-bounce { + 0%, 100% { -webkit-transform: scale(0.0) } + 50% { -webkit-transform: scale(1.0) } +} + +@keyframes sk-bounce { + 0%, 100% { + transform: scale(0.0); + -webkit-transform: scale(0.0); + } 50% { + transform: scale(1.0); + -webkit-transform: scale(1.0); + } +} diff --git a/client/components/main/spinner_double_bounce.jade b/client/components/main/spinner_double_bounce.jade new file mode 100644 index 000000000..206a468d2 --- /dev/null +++ b/client/components/main/spinner_double_bounce.jade @@ -0,0 +1,7 @@ +template(name="spinnerDoubleBounce") + .sk-spinner.sk-spinner-double-bounce(class=currentBoard.colorClass) + +spinnerDoubleBounceRaw + +template(name="spinnerDoubleBounceRaw") + .sk-double-bounce1 + .sk-double-bounce2 diff --git a/client/components/main/spinner_double_bounce.styl b/client/components/main/spinner_double_bounce.styl new file mode 100644 index 000000000..2ef000361 --- /dev/null +++ b/client/components/main/spinner_double_bounce.styl @@ -0,0 +1,44 @@ +@import 'nib' + +// From https://github.com/tobiasahlin/SpinKit +.sk-spinner-double-bounce { + width: 40px; + height: 40px; + + position: relative; + margin: 100px auto; +} + +.sk-double-bounce1, .sk-double-bounce2 { + width: 100%; + height: 100%; + border-radius: 50%; + background-color: #333; + opacity: 0.6; + position: absolute; + top: 0; + left: 0; + + -webkit-animation: sk-bounce 2.0s infinite ease-in-out; + animation: sk-bounce 2.0s infinite ease-in-out; +} + +.sk-double-bounce2 { + -webkit-animation-delay: -1.0s; + animation-delay: -1.0s; +} + +@-webkit-keyframes sk-bounce { + 0%, 100% { -webkit-transform: scale(0.0) } + 50% { -webkit-transform: scale(1.0) } +} + +@keyframes sk-bounce { + 0%, 100% { + transform: scale(0.0); + -webkit-transform: scale(0.0); + } 50% { + transform: scale(1.0); + -webkit-transform: scale(1.0); + } +} diff --git a/client/components/main/spinner_rotateplane.jade b/client/components/main/spinner_rotateplane.jade new file mode 100644 index 000000000..35afeda81 --- /dev/null +++ b/client/components/main/spinner_rotateplane.jade @@ -0,0 +1,6 @@ +template(name="spinnerRotateplane") + .sk-spinner.sk-spinner-rotateplane(class=currentBoard.colorClass) + +spinnerRotateplaneRaw + +template(name="spinnerRotateplaneRaw") + .sk-rotateplane1 diff --git a/client/components/main/spinner_rotateplane.styl b/client/components/main/spinner_rotateplane.styl new file mode 100644 index 000000000..1f43d37e3 --- /dev/null +++ b/client/components/main/spinner_rotateplane.styl @@ -0,0 +1,38 @@ +@import 'nib' + +// From https://github.com/tobiasahlin/SpinKit +.sk-spinner-rotateplane { + width: 40px; + height: 40px; + text-align: center; + + margin: 100px auto; + -webkit-animation: sk-rotateplane 1.2s infinite ease-in-out; + animation: sk-rotateplane 1.2s infinite ease-in-out; + + div { + background-color: #333; + height: 100%; + width: 100%; + display: inline-block; + } +} + +@-webkit-keyframes sk-rotateplane { + 0% { -webkit-transform: perspective(120px) } + 50% { -webkit-transform: perspective(120px) rotateY(180deg) } + 100% { -webkit-transform: perspective(120px) rotateY(180deg) rotateX(180deg) } +} + +@keyframes sk-rotateplane { + 0% { + transform: perspective(120px) rotateX(0deg) rotateY(0deg); + -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg) + } 50% { + transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg); + -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) + } 100% { + transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); + -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); + } +} diff --git a/client/components/main/spinner_scaleout.jade b/client/components/main/spinner_scaleout.jade new file mode 100644 index 000000000..2cb21d156 --- /dev/null +++ b/client/components/main/spinner_scaleout.jade @@ -0,0 +1,6 @@ +template(name="spinnerScaleout") + .sk-spinner.sk-spinner-scaleout(class=currentBoard.colorClass) + +spinnerScaleoutRaw + +template(name="spinnerScaleoutRaw") + .sk-scaleout1 diff --git a/client/components/main/spinner_scaleout.styl b/client/components/main/spinner_scaleout.styl new file mode 100644 index 000000000..deb73b68e --- /dev/null +++ b/client/components/main/spinner_scaleout.styl @@ -0,0 +1,40 @@ +@import 'nib' + +// From https://github.com/tobiasahlin/SpinKit +.sk-spinner-scaleout { + width: 40px; + height: 40px; + text-align: center; + + margin: 100px auto; + + border-radius: 100%; + -webkit-animation: sk-scaleout 1.0s infinite ease-in-out; + animation: sk-scaleout 1.0s infinite ease-in-out; + + div { + background-color: #333; + height: 100%; + width: 100%; + display: inline-block; + } +} + +@-webkit-keyframes sk-scaleout { + 0% { -webkit-transform: scale(0) } + 100% { + -webkit-transform: scale(1.0); + opacity: 0; + } +} + +@keyframes sk-scaleout { + 0% { + -webkit-transform: scale(0); + transform: scale(0); + } 100% { + -webkit-transform: scale(1.0); + transform: scale(1.0); + opacity: 0; + } +} diff --git a/client/components/main/spinner_wave.jade b/client/components/main/spinner_wave.jade new file mode 100644 index 000000000..fe5a26b0d --- /dev/null +++ b/client/components/main/spinner_wave.jade @@ -0,0 +1,15 @@ +template(name="spinnerWave") + .sk-spinner.sk-spinner-wave(class=currentBoard.colorClass) + +spinnerWaveRaw + +template(name="spinnerWaveRaw") + .sk-rect1 + |   + .sk-rect2 + |   + .sk-rect3 + |   + .sk-rect4 + |   + .sk-rect5 + |   diff --git a/client/components/main/spinner.styl b/client/components/main/spinner_wave.styl similarity index 61% rename from client/components/main/spinner.styl rename to client/components/main/spinner_wave.styl index 65c5fe62e..a82d39496 100644 --- a/client/components/main/spinner.styl +++ b/client/components/main/spinner_wave.styl @@ -1,21 +1,7 @@ @import 'nib' -/* - * From https://github.com/tobiasahlin/SpinKit - * - * Usage: - * - *
- *
- *
- *
- *
- *
- *
- * - */ - -.sk-spinner { +// From https://github.com/tobiasahlin/SpinKit +.sk-spinner-wave { width: 50px; height: 50px; margin: auto; diff --git a/client/components/settings/settingBody.jade b/client/components/settings/settingBody.jade index f8cfc3a59..bd69e21ad 100644 --- a/client/components/settings/settingBody.jade +++ b/client/components/settings/settingBody.jade @@ -173,6 +173,9 @@ template(name='layoutSettings') li.layout-form .title {{_ 'default-authentication-method'}} +selectAuthenticationMethod(authenticationMethod=currentSetting.defaultAuthenticationMethod) + li.layout-form + .title {{_ 'wait-spinner'}} + +selectSpinnerName(spinnerName=currentSetting.spinnerName) li.layout-form .title {{_ 'custom-product-name'}} .form-group @@ -222,3 +225,11 @@ template(name='selectAuthenticationMethod') option(value="{{value}}" selected) {{_ value}} else option(value="{{value}}") {{_ value}} + +template(name='selectSpinnerName') + select#spinnerName + each spinner in spinners + if isSelected spinner + option(value="{{spinner}}" selected) {{spinner}} + else + option(value="{{spinner}}") {{spinner}} diff --git a/client/components/settings/settingBody.js b/client/components/settings/settingBody.js index 596b40613..e55e2db6b 100644 --- a/client/components/settings/settingBody.js +++ b/client/components/settings/settingBody.js @@ -1,3 +1,5 @@ +import { ALLOWED_WAIT_SPINNERS } from '/config/const'; + BlazeComponent.extendComponent({ onCreated() { this.error = new ReactiveVar(''); @@ -199,6 +201,8 @@ BlazeComponent.extendComponent({ $('input[name=displayAuthenticationMethod]:checked').val() === 'true'; const defaultAuthenticationMethod = $('#defaultAuthenticationMethod').val(); + const spinnerName = $('#spinnerName').val(); + try { Settings.update(Settings.findOne()._id, { $set: { @@ -213,6 +217,7 @@ BlazeComponent.extendComponent({ displayAuthenticationMethod, defaultAuthenticationMethod, automaticLinkedUrlSchemes, + spinnerName, }, }); } catch (e) { @@ -384,3 +389,12 @@ Template.selectAuthenticationMethod.helpers({ return Template.instance().data.authenticationMethod === match; }, }); + +Template.selectSpinnerName.helpers({ + spinners() { + return ALLOWED_WAIT_SPINNERS; + }, + isSelected(match) { + return Template.instance().data.spinnerName === match; + }, +}); diff --git a/client/lib/spinner.js b/client/lib/spinner.js new file mode 100644 index 000000000..7b52b7c34 --- /dev/null +++ b/client/lib/spinner.js @@ -0,0 +1,27 @@ +Meteor.subscribe('setting'); + +import { ALLOWED_WAIT_SPINNERS } from '/config/const'; + +export class Spinner extends BlazeComponent { + currentSettings() { + return Settings.findOne(); + } + + getSpinnerName() { + let ret = 'Bounce'; + let defaultWaitSpinner = Meteor.settings.public.DEFAULT_WAIT_SPINNER; + if (defaultWaitSpinner && ALLOWED_WAIT_SPINNERS.includes(defaultWaitSpinner)) { + ret = defaultWaitSpinner; + } + let settings = this.currentSettings(); + + if (settings && settings.spinnerName) { + ret = settings.spinnerName; + } + return ret; + } + + getSpinnerTemplate() { + return 'spinner' + this.getSpinnerName().replace(/-/, ''); + } +} diff --git a/config/const.js b/config/const.js index 0c9c71c7c..a275ffa6b 100644 --- a/config/const.js +++ b/config/const.js @@ -49,3 +49,13 @@ export const TYPE_LINKED_BOARD = 'cardType-linkedBoard'; export const TYPE_LINKED_CARD = 'cardType-linkedCard'; export const TYPE_TEMPLATE_BOARD = 'template-board'; export const TYPE_TEMPLATE_CONTAINER = 'template-container'; +export const ALLOWED_WAIT_SPINNERS = [ + 'Bounce', + 'Cube', + 'Cube-Grid', + 'Dot', + 'Double-Bounce', + 'Rotateplane', + 'Scaleout', + 'Wave' +]; diff --git a/docker-compose.yml b/docker-compose.yml index bfcff6f24..6e2570378 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -618,6 +618,9 @@ services: #- SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE= #- SAML_ATTRIBUTES= #--------------------------------------------------------------------- + # Default wait spinner to use + # - DEFAULT_WAIT_SPINNER=Bounce + #--------------------------------------------------------------------- depends_on: - wekandb diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index ef5d15c8e..11920de5e 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -1039,5 +1039,6 @@ "rulesReportTitle": "Rules Report", "copy-swimlane": "Copy Swimlane", "copySwimlanePopup-title": "Copy Swimlane", - "display-card-creator": "Display Card Creator" + "display-card-creator": "Display Card Creator", + "wait-spinner": "Wait Spinner" } diff --git a/models/settings.js b/models/settings.js index db69644fb..286ef29f9 100644 --- a/models/settings.js +++ b/models/settings.js @@ -46,6 +46,10 @@ Settings.attachSchema( type: String, optional: false, }, + spinnerName: { + type: String, + optional: true, + }, hideLogo: { type: Boolean, optional: true, diff --git a/releases/virtualbox/start-wekan.sh b/releases/virtualbox/start-wekan.sh index 728358d7f..08d145f5f 100755 --- a/releases/virtualbox/start-wekan.sh +++ b/releases/virtualbox/start-wekan.sh @@ -382,6 +382,9 @@ #export SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE= #export SAML_ATTRIBUTES= #--------------------------------------------------------------------- + # Default wait spinner to use + #export DEFAULT_WAIT_SPINNER=Bounce + #--------------------------------------------------------------------- node main.js & >> ~/repos/wekan.log cd ~/repos diff --git a/server/publications/settings.js b/server/publications/settings.js index 829824dba..84234e31b 100644 --- a/server/publications/settings.js +++ b/server/publications/settings.js @@ -23,6 +23,7 @@ Meteor.publish('setting', () => { customHTMLbeforeBodyEnd: 1, displayAuthenticationMethod: 1, defaultAuthenticationMethod: 1, + spinnerName: 1, }, }, ); diff --git a/server/spinner.js b/server/spinner.js new file mode 100644 index 000000000..316641392 --- /dev/null +++ b/server/spinner.js @@ -0,0 +1,3 @@ +Meteor.startup(() => { + Meteor.settings.public.DEFAULT_WAIT_SPINNER = process.env.DEFAULT_WAIT_SPINNER; +}); diff --git a/snap-src/bin/config b/snap-src/bin/config index 52c46f365..9f0680859 100755 --- a/snap-src/bin/config +++ b/snap-src/bin/config @@ -3,7 +3,7 @@ # All supported keys are defined here together with descriptions and default values # list of supported keys -keys="DEBUG MONGO_LOG_DESTINATION MONGO_URL MONGODB_BIND_UNIX_SOCKET MONGO_URL MONGODB_BIND_IP MONGODB_PORT MAIL_URL MAIL_FROM ROOT_URL PORT DISABLE_MONGODB CADDY_ENABLED CADDY_BIND_PORT WITH_API RICHER_CARD_COMMENT_EDITOR CARD_OPENED_WEBHOOK_ENABLED ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW MAX_IMAGE_PIXEL IMAGE_COMPRESS_RATIO BIGEVENTS_PATTERN NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE NOTIFY_DUE_DAYS_BEFORE_AND_AFTER NOTIFY_DUE_AT_HOUR_OF_DAY EMAIL_NOTIFICATION_TIMEOUT CORS CORS_ALLOW_HEADERS CORS_EXPOSE_HEADERS MATOMO_ADDRESS MATOMO_SITE_ID MATOMO_DO_NOT_TRACK MATOMO_WITH_USERNAME BROWSER_POLICY_ENABLED TRUSTED_URL WEBHOOKS_ATTRIBUTES OAUTH2_ENABLED OAUTH2_CA_CERT OAUTH2_LOGIN_STYLE OAUTH2_CLIENT_ID OAUTH2_SECRET OAUTH2_SERVER_URL OAUTH2_AUTH_ENDPOINT OAUTH2_USERINFO_ENDPOINT OAUTH2_TOKEN_ENDPOINT OAUTH2_ID_MAP OAUTH2_USERNAME_MAP OAUTH2_FULLNAME_MAP OAUTH2_ID_TOKEN_WHITELIST_FIELDS OAUTH2_EMAIL_MAP OAUTH2_REQUEST_PERMISSIONS OAUTH2_ADFS_ENABLED LDAP_ENABLE LDAP_PORT LDAP_HOST LDAP_BASEDN LDAP_LOGIN_FALLBACK LDAP_RECONNECT LDAP_TIMEOUT LDAP_IDLE_TIMEOUT LDAP_CONNECT_TIMEOUT LDAP_AUTHENTIFICATION LDAP_AUTHENTIFICATION_USERDN LDAP_AUTHENTIFICATION_PASSWORD LDAP_LOG_ENABLED LDAP_BACKGROUND_SYNC LDAP_BACKGROUND_SYNC_INTERVAL LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS LDAP_ENCRYPTION LDAP_CA_CERT LDAP_REJECT_UNAUTHORIZED LDAP_USER_AUTHENTICATION LDAP_USER_AUTHENTICATION_FIELD LDAP_USER_SEARCH_FILTER LDAP_USER_SEARCH_SCOPE LDAP_USER_SEARCH_FIELD LDAP_SEARCH_PAGE_SIZE LDAP_SEARCH_SIZE_LIMIT LDAP_GROUP_FILTER_ENABLE LDAP_GROUP_FILTER_OBJECTCLASS LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT LDAP_GROUP_FILTER_GROUP_NAME LDAP_UNIQUE_IDENTIFIER_FIELD LDAP_UTF8_NAMES_SLUGIFY LDAP_USERNAME_FIELD LDAP_FULLNAME_FIELD LDAP_MERGE_EXISTING_USERS LDAP_SYNC_USER_DATA LDAP_SYNC_USER_DATA_FIELDMAP LDAP_SYNC_GROUP_ROLES LDAP_DEFAULT_DOMAIN LDAP_EMAIL_MATCH_ENABLE LDAP_EMAIL_MATCH_REQUIRE LDAP_EMAIL_MATCH_VERIFIED LDAP_EMAIL_FIELD LDAP_SYNC_ADMIN_STATUS LDAP_SYNC_ADMIN_GROUPS HEADER_LOGIN_ID HEADER_LOGIN_FIRSTNAME HEADER_LOGIN_LASTNAME HEADER_LOGIN_EMAIL LOGOUT_WITH_TIMER LOGOUT_IN LOGOUT_ON_HOURS LOGOUT_ON_MINUTES DEFAULT_AUTHENTICATION_METHOD ATTACHMENTS_STORE_PATH PASSWORD_LOGIN_ENABLED CAS_ENABLED CAS_BASE_URL CAS_LOGIN_URL CAS_VALIDATE_URL SAML_ENABLED SAML_PROVIDER SAML_ENTRYPOINT SAML_ISSUER SAML_CERT SAML_IDPSLO_REDIRECTURL SAML_PRIVATE_KEYFILE SAML_PUBLIC_CERTFILE SAML_IDENTIFIER_FORMAT SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE SAML_ATTRIBUTES ORACLE_OIM_ENABLED RESULTS_PER_PAGE" +keys="DEBUG MONGO_LOG_DESTINATION MONGO_URL MONGODB_BIND_UNIX_SOCKET MONGO_URL MONGODB_BIND_IP MONGODB_PORT MAIL_URL MAIL_FROM ROOT_URL PORT DISABLE_MONGODB CADDY_ENABLED CADDY_BIND_PORT WITH_API RICHER_CARD_COMMENT_EDITOR CARD_OPENED_WEBHOOK_ENABLED ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW MAX_IMAGE_PIXEL IMAGE_COMPRESS_RATIO BIGEVENTS_PATTERN NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE NOTIFY_DUE_DAYS_BEFORE_AND_AFTER NOTIFY_DUE_AT_HOUR_OF_DAY EMAIL_NOTIFICATION_TIMEOUT CORS CORS_ALLOW_HEADERS CORS_EXPOSE_HEADERS MATOMO_ADDRESS MATOMO_SITE_ID MATOMO_DO_NOT_TRACK MATOMO_WITH_USERNAME BROWSER_POLICY_ENABLED TRUSTED_URL WEBHOOKS_ATTRIBUTES OAUTH2_ENABLED OAUTH2_CA_CERT OAUTH2_LOGIN_STYLE OAUTH2_CLIENT_ID OAUTH2_SECRET OAUTH2_SERVER_URL OAUTH2_AUTH_ENDPOINT OAUTH2_USERINFO_ENDPOINT OAUTH2_TOKEN_ENDPOINT OAUTH2_ID_MAP OAUTH2_USERNAME_MAP OAUTH2_FULLNAME_MAP OAUTH2_ID_TOKEN_WHITELIST_FIELDS OAUTH2_EMAIL_MAP OAUTH2_REQUEST_PERMISSIONS OAUTH2_ADFS_ENABLED LDAP_ENABLE LDAP_PORT LDAP_HOST LDAP_BASEDN LDAP_LOGIN_FALLBACK LDAP_RECONNECT LDAP_TIMEOUT LDAP_IDLE_TIMEOUT LDAP_CONNECT_TIMEOUT LDAP_AUTHENTIFICATION LDAP_AUTHENTIFICATION_USERDN LDAP_AUTHENTIFICATION_PASSWORD LDAP_LOG_ENABLED LDAP_BACKGROUND_SYNC LDAP_BACKGROUND_SYNC_INTERVAL LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS LDAP_ENCRYPTION LDAP_CA_CERT LDAP_REJECT_UNAUTHORIZED LDAP_USER_AUTHENTICATION LDAP_USER_AUTHENTICATION_FIELD LDAP_USER_SEARCH_FILTER LDAP_USER_SEARCH_SCOPE LDAP_USER_SEARCH_FIELD LDAP_SEARCH_PAGE_SIZE LDAP_SEARCH_SIZE_LIMIT LDAP_GROUP_FILTER_ENABLE LDAP_GROUP_FILTER_OBJECTCLASS LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT LDAP_GROUP_FILTER_GROUP_NAME LDAP_UNIQUE_IDENTIFIER_FIELD LDAP_UTF8_NAMES_SLUGIFY LDAP_USERNAME_FIELD LDAP_FULLNAME_FIELD LDAP_MERGE_EXISTING_USERS LDAP_SYNC_USER_DATA LDAP_SYNC_USER_DATA_FIELDMAP LDAP_SYNC_GROUP_ROLES LDAP_DEFAULT_DOMAIN LDAP_EMAIL_MATCH_ENABLE LDAP_EMAIL_MATCH_REQUIRE LDAP_EMAIL_MATCH_VERIFIED LDAP_EMAIL_FIELD LDAP_SYNC_ADMIN_STATUS LDAP_SYNC_ADMIN_GROUPS HEADER_LOGIN_ID HEADER_LOGIN_FIRSTNAME HEADER_LOGIN_LASTNAME HEADER_LOGIN_EMAIL LOGOUT_WITH_TIMER LOGOUT_IN LOGOUT_ON_HOURS LOGOUT_ON_MINUTES DEFAULT_AUTHENTICATION_METHOD ATTACHMENTS_STORE_PATH PASSWORD_LOGIN_ENABLED CAS_ENABLED CAS_BASE_URL CAS_LOGIN_URL CAS_VALIDATE_URL SAML_ENABLED SAML_PROVIDER SAML_ENTRYPOINT SAML_ISSUER SAML_CERT SAML_IDPSLO_REDIRECTURL SAML_PRIVATE_KEYFILE SAML_PUBLIC_CERTFILE SAML_IDENTIFIER_FORMAT SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE SAML_ATTRIBUTES ORACLE_OIM_ENABLED RESULTS_PER_PAGE DEFAULT_WAIT_SPINNER" # default values DESCRIPTION_DEBUG="Debug OIDC OAuth2 etc. Example: sudo snap set wekan debug='true'" @@ -547,3 +547,7 @@ KEY_SAML_ATTRIBUTES="saml-attributes" DESCRIPTION_RESULTS_PER_PAGE="Number of search results to show per page by default" DEFAULT_RESULTS_PER_PAGE="" KEY_RESULTS_PER_PAGE="results-per-page" + +DESCRIPTION_WAIT_SPINNER="Default wait spinner to use" +DEFAULT_WAIT_SPINNER="Bounce" +KEY_WAIT_SPINNER="wait-spinner" diff --git a/start-wekan.bat b/start-wekan.bat index 5779fd6fc..9399e0cec 100644 --- a/start-wekan.bat +++ b/start-wekan.bat @@ -418,4 +418,6 @@ REM SET SAML_IDENTIFIER_FORMAT= REM SET SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE= REM SET SAML_ATTRIBUTES= +REM SET DEFAULT_WAIT_SPINNER= + node main.js diff --git a/start-wekan.sh b/start-wekan.sh index bbdb4e26c..da1b6fc0a 100755 --- a/start-wekan.sh +++ b/start-wekan.sh @@ -388,6 +388,9 @@ #export SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE= #export SAML_ATTRIBUTES= #--------------------------------------------------------------------- + # Default wait spinner to use + #export DEFAULT_WAIT_SPINNER=Bounce + #--------------------------------------------------------------------- node main.js # & >> ../../wekan.log cd ../.. diff --git a/torodb-postgresql/docker-compose.yml b/torodb-postgresql/docker-compose.yml index d6497ac4d..8bfb11f71 100644 --- a/torodb-postgresql/docker-compose.yml +++ b/torodb-postgresql/docker-compose.yml @@ -559,6 +559,9 @@ services: #- SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE= #- SAML_ATTRIBUTES= #--------------------------------------------------------------------- + # Default wait spinner to use + # - DEFAULT_WAIT_SPINNER=Bounce + #--------------------------------------------------------------------- depends_on: - mongodb