Skip to content

Commit

Permalink
UX: Refactor AI summarizing animation (#22839)
Browse files Browse the repository at this point in the history
  • Loading branch information
jordanvidrine committed Jul 28, 2023
1 parent 331507f commit 0f1479e
Show file tree
Hide file tree
Showing 4 changed files with 277 additions and 61 deletions.
@@ -0,0 +1,26 @@
<ul class="ai-summary__list" {{did-insert this.setupAnimation}}>
{{#each this.blocks as |block|}}
<li
class={{concat-class
"ai-summary__list-item"
(if block.show "show")
(if block.shown "is-shown")
(if block.blinking "blink")
}}
{{did-update (fn this.onBlinking block) block.blinking}}
{{did-update (fn this.onShowing block) block.show}}
{{will-destroy this.teardownAnimation}}
></li>
{{/each}}
</ul>

<span>
<div class="ai-summary__generating-text">
{{i18n "summary.in_progress"}}
</div>
<span class="ai-summary__indicator-wave">
<span class="ai-summary__indicator-dot">.</span>
<span class="ai-summary__indicator-dot">.</span>
<span class="ai-summary__indicator-dot">.</span>
</span>
</span>
@@ -0,0 +1,92 @@
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);
}
}
}
43 changes: 9 additions & 34 deletions app/assets/javascripts/discourse/app/widgets/summary-box.js
Expand Up @@ -10,39 +10,6 @@ import { h } from "virtual-dom";
import { iconNode } from "discourse-common/lib/icon-library";
import RenderGlimmer from "discourse/widgets/render-glimmer";

createWidget("summary-skeleton", {
tagName: "section.placeholder-summary",

html() {
const html = [];

html.push(this.buildPlaceholderDiv());
html.push(this.buildPlaceholderDiv());
html.push(this.buildPlaceholderDiv());

html.push(
h("span", {}, [
h(
"div.placeholder-generating-summary-text",
{},
I18n.t("summary.in_progress")
),
h("span.ai-summarizing-indicator__wave", {}, [
h("span.ai-summarizing-indicator__dot", "."),
h("span.ai-summarizing-indicator__dot", "."),
h("span.ai-summarizing-indicator__dot", "."),
]),
])
);

return html;
},

buildPlaceholderDiv() {
return h("div.placeholder-summary-text.placeholder-animation");
},
});

export default createWidget("summary-box", {
tagName: "article.summary-box",
buildKey: (attrs) => `summary-box-${attrs.topicId}`,
Expand Down Expand Up @@ -76,13 +43,21 @@ export default createWidget("summary-box", {

html.push(h("div.summarized-on", {}, summarizationInfo));
} else {
html.push(this.attach("summary-skeleton"));
html.push(this.buildSummarySkeleton());
this.fetchSummary(attrs.topicId, attrs.skipAgeCheck);
}

return html;
},

buildSummarySkeleton() {
return new RenderGlimmer(
this,
"div.ai-summary__container",
hbs`{{ai-summary-skeleton}}`
);
},

buildTooltip(attrs) {
return new RenderGlimmer(
this,
Expand Down
177 changes: 150 additions & 27 deletions app/assets/stylesheets/common/base/topic-summary.scss
@@ -1,56 +1,158 @@
.topic-map {
.toggle-summary {
.summarization-buttons {
.topic-map .toggle-summary {
.summarization-buttons {
display: flex;
}

.ai-summary {
&__list {
list-style: none;
display: flex;
flex-wrap: wrap;
padding: 0;
margin: 0;
}
&__list-item {
background: var(--primary-300);
border-radius: var(--d-border-radius);
margin-right: 8px;
margin-bottom: 8px;
height: 18px;
opacity: 0;
display: block;
&:nth-child(1) {
width: 10%;
}

.placeholder-summary {
padding-top: 0.5em;
}
&:nth-child(2) {
width: 12%;
}

.placeholder-summary-text {
display: inline-block;
height: 1em;
margin-top: 0.6em;
width: 100%;
}
&:nth-child(3) {
width: 18%;
}

&:nth-child(4) {
width: 14%;
}

&:nth-child(5) {
width: 18%;
}

&:nth-child(6) {
width: 14%;
}

&:nth-child(7) {
width: 22%;
}

.placeholder-generating-summary-text {
&: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;
}
}
&__generating-text {
display: inline-block;
margin-left: 3px;
}

.ai-summarizing-indicator__wave {
&__indicator-wave {
flex: 0 0 auto;
display: inline-flex;
}

.ai-summarizing-indicator__dot {
&__indicator-dot {
display: inline-block;
animation: ai-summarizing-indicator__wave 1.8s linear infinite;
animation: ai-summary__indicator-wave 1.8s linear infinite;
&:nth-child(2) {
animation-delay: -1.6s;
}
&:nth-child(3) {
animation-delay: -1.4s;
}
}
}

.summarized-on {
text-align: right;
.placeholder-summary {
padding-top: 0.5em;
}

.info-icon {
margin-left: 3px;
}
}
.placeholder-summary-text {
display: inline-block;
height: 1em;
margin-top: 0.6em;
width: 100%;
}

.summarized-on {
text-align: right;

.outdated-summary {
color: var(--primary-medium);
.info-icon {
margin-left: 3px;
}
}

.outdated-summary {
color: var(--primary-medium);
}
}

@keyframes ai-summarizing-indicator__wave {
@keyframes ai-summary__indicator-wave {
0%,
60%,
100% {
Expand All @@ -60,3 +162,24 @@
transform: translateY(-0.2em);
}
}

@keyframes appear {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}

@keyframes blink {
0% {
opacity: 1;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}

0 comments on commit 0f1479e

Please sign in to comment.