From bd19f0c9f1c892255dc2cd6f3c6000eb60a4a20c Mon Sep 17 00:00:00 2001 From: Keegan George Date: Thu, 3 Aug 2023 13:59:35 -0700 Subject: [PATCH] UX: Simplify and redesign summary skeleton (#22965) --- .../discourse-common/addon/helpers/i18n.js | 4 + .../app/components/ai-summary-skeleton.gjs | 35 ++++ .../app/components/ai-summary-skeleton.hbs | 26 --- .../app/components/ai-summary-skeleton.js | 92 --------- .../discourse/app/widgets/summary-box.js | 2 +- .../common/base/topic-summary.scss | 183 +++++++----------- 6 files changed, 110 insertions(+), 232 deletions(-) create mode 100644 app/assets/javascripts/discourse/app/components/ai-summary-skeleton.gjs delete mode 100644 app/assets/javascripts/discourse/app/components/ai-summary-skeleton.hbs delete mode 100644 app/assets/javascripts/discourse/app/components/ai-summary-skeleton.js diff --git a/app/assets/javascripts/discourse-common/addon/helpers/i18n.js b/app/assets/javascripts/discourse-common/addon/helpers/i18n.js index f9fc65aa8da..17e7509ac00 100644 --- a/app/assets/javascripts/discourse-common/addon/helpers/i18n.js +++ b/app/assets/javascripts/discourse-common/addon/helpers/i18n.js @@ -1,6 +1,10 @@ import I18n from "I18n"; import { registerUnbound } from "discourse-common/lib/helpers"; +export default function i18n(key, params) { + return I18n.t(key, params); +} + registerUnbound("i18n", (key, params) => I18n.t(key, params)); registerUnbound("i18n-yes-no", (value, params) => I18n.t(value ? "yes_value" : "no_value", params) diff --git a/app/assets/javascripts/discourse/app/components/ai-summary-skeleton.gjs b/app/assets/javascripts/discourse/app/components/ai-summary-skeleton.gjs new file mode 100644 index 00000000000..3168be9b577 --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/ai-summary-skeleton.gjs @@ -0,0 +1,35 @@ +import i18n from "discourse-common/helpers/i18n"; + + diff --git a/app/assets/javascripts/discourse/app/components/ai-summary-skeleton.hbs b/app/assets/javascripts/discourse/app/components/ai-summary-skeleton.hbs deleted file mode 100644 index 410f6376e0b..00000000000 --- a/app/assets/javascripts/discourse/app/components/ai-summary-skeleton.hbs +++ /dev/null @@ -1,26 +0,0 @@ - - - -
- {{i18n "summary.in_progress"}} -
- - . - . - . - -
\ No newline at end of file diff --git a/app/assets/javascripts/discourse/app/components/ai-summary-skeleton.js b/app/assets/javascripts/discourse/app/components/ai-summary-skeleton.js deleted file mode 100644 index 1323a7b3bf9..00000000000 --- a/app/assets/javascripts/discourse/app/components/ai-summary-skeleton.js +++ /dev/null @@ -1,92 +0,0 @@ -import Component from "@glimmer/component"; -import { action } from "@ember/object"; -import { tracked } from "@glimmer/tracking"; -import discourseLater from "discourse-common/lib/later"; -import { cancel } from "@ember/runloop"; - -class Block { - @tracked show = false; - @tracked shown = false; - @tracked blinking = false; - - constructor(args = {}) { - this.show = args.show ?? false; - this.shown = args.shown ?? false; - } -} - -const BLOCKS_SIZE = 20; // changing this requires to change css accordingly - -export default class AiSummarySkeleton extends Component { - blocks = [...Array.from({ length: BLOCKS_SIZE }, () => new Block())]; - - #nextBlockBlinkingTimer; - #blockBlinkingTimer; - #blockShownTimer; - - @action - setupAnimation() { - this.blocks.firstObject.show = true; - this.blocks.firstObject.shown = true; - } - - @action - onBlinking(block) { - if (!block.blinking) { - return; - } - - block.show = false; - - this.#nextBlockBlinkingTimer = discourseLater( - this, - () => { - this.#nextBlock(block).blinking = true; - }, - 250 - ); - - this.#blockBlinkingTimer = discourseLater( - this, - () => { - block.blinking = false; - }, - 500 - ); - } - - @action - onShowing(block) { - if (!block.show) { - return; - } - - this.#blockShownTimer = discourseLater( - this, - () => { - this.#nextBlock(block).show = true; - this.#nextBlock(block).shown = true; - - if (this.blocks.lastObject === block) { - this.blocks.firstObject.blinking = true; - } - }, - 250 - ); - } - - @action - teardownAnimation() { - cancel(this.#blockShownTimer); - cancel(this.#nextBlockBlinkingTimer); - cancel(this.#blockBlinkingTimer); - } - - #nextBlock(currentBlock) { - if (currentBlock === this.blocks.lastObject) { - return this.blocks.firstObject; - } else { - return this.blocks.objectAt(this.blocks.indexOf(currentBlock) + 1); - } - } -} diff --git a/app/assets/javascripts/discourse/app/widgets/summary-box.js b/app/assets/javascripts/discourse/app/widgets/summary-box.js index fa0e425a1b6..b8254386622 100644 --- a/app/assets/javascripts/discourse/app/widgets/summary-box.js +++ b/app/assets/javascripts/discourse/app/widgets/summary-box.js @@ -54,7 +54,7 @@ export default createWidget("summary-box", { return new RenderGlimmer( this, "div.ai-summary__container", - hbs`{{ai-summary-skeleton}}` + hbs`` ); }, diff --git a/app/assets/stylesheets/common/base/topic-summary.scss b/app/assets/stylesheets/common/base/topic-summary.scss index 781f8ddc37b..7fd4b66d8c9 100644 --- a/app/assets/stylesheets/common/base/topic-summary.scss +++ b/app/assets/stylesheets/common/base/topic-summary.scss @@ -10,120 +10,87 @@ flex-wrap: wrap; padding: 0; margin: 0; + position: relative; + overflow: hidden; } + &__list-item { background: var(--primary-300); border-radius: var(--d-border-radius); margin-right: 8px; margin-bottom: 8px; height: 18px; - opacity: 0; display: block; + animation: motion-up 1.5s infinite linear; + &:nth-child(1) { - width: 10%; + width: 100%; } &:nth-child(2) { - width: 12%; + width: 85%; } &:nth-child(3) { - width: 18%; + width: 90%; } &:nth-child(4) { - width: 14%; - } - - &:nth-child(5) { - width: 18%; - } - - &:nth-child(6) { - width: 14%; - } - - &:nth-child(7) { - width: 22%; - } - - &:nth-child(8) { - width: 05%; - } - - &:nth-child(9) { - width: 25%; - } - - &:nth-child(10) { - width: 14%; - } - - &:nth-child(11) { - width: 18%; - } - - &:nth-child(12) { - width: 12%; - } - - &:nth-child(13) { - width: 22%; - } - - &:nth-child(14) { - width: 18%; - } - - &:nth-child(15) { - width: 13%; - } - - &:nth-child(16) { - width: 22%; - } - - &:nth-child(17) { - width: 19%; - } - - &:nth-child(18) { - width: 13%; - } - - &:nth-child(19) { - width: 22%; - } - - &:nth-child(20) { - width: 25%; - } - &.is-shown { - opacity: 1; - } - &.show { - animation: appear 0.5s cubic-bezier(0.445, 0.05, 0.55, 0.95) 0s forwards; - } - &.blink { - animation: blink 0.5s cubic-bezier(0.55, 0.085, 0.68, 0.53) both; + width: 95%; } } - &__generating-text { - display: inline-block; - margin-left: 3px; + + &__shimmer { + position: absolute; + top: 0; + left: 0; + width: 50%; + height: 100%; + background: linear-gradient( + 100deg, + rgba(255, 255, 255, 0) 20%, + rgba(255, 255, 255, 0.5) 50%, + rgba(255, 255, 255, 0) 80% + ); + + animation: ai-shimmer 1.5s infinite linear; } - &__indicator-wave { - flex: 0 0 auto; - display: inline-flex; + + &__status { + display: flex; + align-items: center; + gap: 0.5rem; } - &__indicator-dot { - display: inline-block; - animation: ai-summary__indicator-wave 1.8s linear infinite; - &:nth-child(2) { - animation-delay: -1.6s; - } - &:nth-child(3) { - animation-delay: -1.4s; + + &__sparkles { + position: relative; + width: 30px; + height: 30px; + + svg { + margin: 0; + padding: 0; + position: absolute; + + path { + position: absolute; + transform-origin: center; + scale: 0.9; + opacity: 0.5; + fill: var(--tertiary); + &:nth-child(1) { + animation: sparkle 2.25s infinite + cubic-bezier(0.175, 0.885, 0.32, 1.275); + } + &:nth-child(2) { + animation: sparkle 3.25s infinite + cubic-bezier(0.175, 0.885, 0.32, 1.275); + } + &:nth-child(3) { + animation: sparkle 4.25s infinite + cubic-bezier(0.175, 0.885, 0.32, 1.275); + } + } } } } @@ -152,34 +119,24 @@ } } -@keyframes ai-summary__indicator-wave { - 0%, - 60%, - 100% { - transform: initial; +@keyframes ai-shimmer { + 0% { + transform: translateX(-200%); } - 30% { - transform: translateY(-0.2em); + 100% { + transform: translateX(200%); } } -@keyframes appear { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } -} - -@keyframes blink { - 0% { - opacity: 1; - } - 50% { +@keyframes sparkle { + 75% { + transform: scale(0.9); opacity: 0.5; + fill: var(--tertiary-medium); } 100% { + transform: scale(1); opacity: 1; + fill: var(--tertiary); } }