mirror of
https://github.com/discourse/discourse.git
synced 2025-03-09 14:34:35 +00:00
UX: Refactor AI summarizing animation (#22839)
This commit is contained in:
parent
331507f6b0
commit
0f1479e896
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,39 +10,6 @@ import { h } from "virtual-dom";
|
|||||||
import { iconNode } from "discourse-common/lib/icon-library";
|
import { iconNode } from "discourse-common/lib/icon-library";
|
||||||
import RenderGlimmer from "discourse/widgets/render-glimmer";
|
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", {
|
export default createWidget("summary-box", {
|
||||||
tagName: "article.summary-box",
|
tagName: "article.summary-box",
|
||||||
buildKey: (attrs) => `summary-box-${attrs.topicId}`,
|
buildKey: (attrs) => `summary-box-${attrs.topicId}`,
|
||||||
@ -76,13 +43,21 @@ export default createWidget("summary-box", {
|
|||||||
|
|
||||||
html.push(h("div.summarized-on", {}, summarizationInfo));
|
html.push(h("div.summarized-on", {}, summarizationInfo));
|
||||||
} else {
|
} else {
|
||||||
html.push(this.attach("summary-skeleton"));
|
html.push(this.buildSummarySkeleton());
|
||||||
this.fetchSummary(attrs.topicId, attrs.skipAgeCheck);
|
this.fetchSummary(attrs.topicId, attrs.skipAgeCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
buildSummarySkeleton() {
|
||||||
|
return new RenderGlimmer(
|
||||||
|
this,
|
||||||
|
"div.ai-summary__container",
|
||||||
|
hbs`{{ai-summary-skeleton}}`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
buildTooltip(attrs) {
|
buildTooltip(attrs) {
|
||||||
return new RenderGlimmer(
|
return new RenderGlimmer(
|
||||||
this,
|
this,
|
||||||
|
@ -1,33 +1,124 @@
|
|||||||
.topic-map {
|
.topic-map .toggle-summary {
|
||||||
.toggle-summary {
|
.summarization-buttons {
|
||||||
.summarization-buttons {
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-summary {
|
||||||
|
&__list {
|
||||||
|
list-style: none;
|
||||||
display: flex;
|
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 {
|
&:nth-child(2) {
|
||||||
padding-top: 0.5em;
|
width: 12%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&: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%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&: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 {
|
||||||
.placeholder-summary-text {
|
|
||||||
display: inline-block;
|
|
||||||
height: 1em;
|
|
||||||
margin-top: 0.6em;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.placeholder-generating-summary-text {
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
}
|
}
|
||||||
|
&__indicator-wave {
|
||||||
.ai-summarizing-indicator__wave {
|
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
}
|
}
|
||||||
|
&__indicator-dot {
|
||||||
.ai-summarizing-indicator__dot {
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
animation: ai-summarizing-indicator__wave 1.8s linear infinite;
|
animation: ai-summary__indicator-wave 1.8s linear infinite;
|
||||||
&:nth-child(2) {
|
&:nth-child(2) {
|
||||||
animation-delay: -1.6s;
|
animation-delay: -1.6s;
|
||||||
}
|
}
|
||||||
@ -35,22 +126,33 @@
|
|||||||
animation-delay: -1.4s;
|
animation-delay: -1.4s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.summarized-on {
|
.placeholder-summary {
|
||||||
text-align: right;
|
padding-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
.info-icon {
|
.placeholder-summary-text {
|
||||||
margin-left: 3px;
|
display: inline-block;
|
||||||
}
|
height: 1em;
|
||||||
|
margin-top: 0.6em;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summarized-on {
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
.info-icon {
|
||||||
|
margin-left: 3px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.outdated-summary {
|
.outdated-summary {
|
||||||
color: var(--primary-medium);
|
color: var(--primary-medium);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes ai-summarizing-indicator__wave {
|
@keyframes ai-summary__indicator-wave {
|
||||||
0%,
|
0%,
|
||||||
60%,
|
60%,
|
||||||
100% {
|
100% {
|
||||||
@ -60,3 +162,24 @@
|
|||||||
transform: translateY(-0.2em);
|
transform: translateY(-0.2em);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes appear {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blink {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user