UX: update gist toggle styles (#926)

This commit is contained in:
Kris 2024-11-19 15:33:34 -05:00 committed by GitHub
parent 530a795d43
commit a9afa04329
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 186 additions and 164 deletions

View File

@ -4,35 +4,43 @@ import { action } from "@ember/object";
import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import DropdownMenu from "discourse/components/dropdown-menu";
import bodyClass from "discourse/helpers/body-class";
import icon from "discourse-common/helpers/d-icon";
import i18n from "discourse-common/helpers/i18n";
import DMenu from "float-kit/components/d-menu";
import eq from "truth-helpers/helpers/eq";
export default class AiGistToggle extends Component {
@service router;
@service gistPreference;
get shouldShow() {
return this.router.currentRoute.attributes?.list?.topics?.some(
(topic) => topic.ai_topic_gist
);
}
@service gists;
get buttons() {
return [
{
id: "gists_enabled",
label: "discourse_ai.summarization.gists_enabled_long",
icon: "discourse-sparkles",
id: "table",
label: "discourse_ai.summarization.topic_list_layout.button.compact",
icon: "discourse-table",
},
{
id: "gists_disabled",
label: "discourse_ai.summarization.gists_disabled",
icon: "far-eye-slash",
id: "table-ai",
label: "discourse_ai.summarization.topic_list_layout.button.expanded",
icon: "discourse-table-sparkles",
description:
"discourse_ai.summarization.topic_list_layout.button.expanded_description",
},
];
}
get selectedOptionId() {
return this.gists.get("preference");
}
get currentButton() {
const buttonPreference = this.buttons.find(
(button) => button.id === this.selectedOptionId
);
return buttonPreference || this.buttons[0];
}
@action
onRegisterApi(api) {
this.dMenu = api;
@ -40,29 +48,22 @@ export default class AiGistToggle extends Component {
@action
onSelect(optionId) {
this.gistPreference.setPreference(optionId);
this.gists.setPreference(optionId);
this.dMenu.close();
}
<template>
{{#if this.shouldShow}}
{{#if this.gists.shouldShow}}
{{bodyClass (concat "topic-list-layout-" this.gists.preference)}}
<DMenu
@modalForMobile={{true}}
@autofocus={{true}}
@identifier="ai-gists-dropdown"
@identifier="topic-list-layout"
@onRegisterApi={{this.onRegisterApi}}
@triggerClass="btn-transparent"
@triggerClass="btn-default btn-icon"
>
<:trigger>
<span class="d-button-label">
{{i18n
(concat
"discourse_ai.summarization." this.gistPreference.preference
)
}}
</span>
{{icon "angle-down"}}
{{icon this.currentButton.icon}}
</:trigger>
<:content>
<DropdownMenu as |dropdown|>
@ -71,9 +72,17 @@ export default class AiGistToggle extends Component {
<DButton
@label={{button.label}}
@icon={{button.icon}}
class="btn-transparent"
class="btn-transparent
{{if button.description '--with-description'}}
{{if (eq this.currentButton.id button.id) '--active'}}"
@action={{fn this.onSelect button.id}}
/>
>
{{#if button.description}}
<div class="btn__description">
{{i18n button.description}}
</div>
{{/if}}
</DButton>
</dropdown.item>
{{/each}}
</DropdownMenu>

View File

@ -1,31 +1,30 @@
import Component from "@glimmer/component";
import { service } from "@ember/service";
import bodyClass from "discourse/helpers/body-class";
import { htmlSafe } from "@ember/template";
import { emojiUnescape, sanitize } from "discourse/lib/text";
export default class AiTopicGist extends Component {
@service router;
@service gistPreference;
@service gists;
get prefersGist() {
return this.gistPreference.preference === "gists_enabled";
get shouldShow() {
return this.gists.preference === "table-ai" && this.gists.shouldShow;
}
get showGist() {
return (
this.args.topic?.ai_topic_gist &&
this.prefersGist &&
!this.args.topic?.excerpt
);
get gistOrExcerpt() {
const topic = this.args.topic;
const gist = topic.get("ai_topic_gist");
const excerpt = emojiUnescape(sanitize(topic.get("excerpt")));
return gist || excerpt;
}
<template>
{{#if this.showGist}}
{{bodyClass "--topic-list-with-gist"}}
<div class="ai-topic-gist">
<div class="ai-topic-gist__text">
{{@topic.ai_topic_gist}}
{{#if this.shouldShow}}
{{#if this.gistOrExcerpt}}
<div class="excerpt">
<div>{{htmlSafe this.gistOrExcerpt}}</div>
</div>
</div>
{{/if}}
{{/if}}
</template>
}

View File

@ -0,0 +1,15 @@
import Component from "@glimmer/component";
import { service } from "@ember/service";
import AiGistToggle from "../../components/ai-gist-toggle";
export default class AiTopicGist extends Component {
@service topicThumbnails; // avoid Topic Thumbnails theme component
get shouldShow() {
return !this.topicThumbnails?.enabledForRoute;
}
<template>
<AiGistToggle />
</template>
}

View File

@ -1,13 +0,0 @@
import Component from "@glimmer/component";
import AiGistToggle from "../../components/ai-gist-toggle";
export default class AiTopicGistToggle extends Component {
static shouldRender(outletArgs, helper) {
const isMobileView = helper.site.mobileView;
return isMobileView;
}
<template>
<AiGistToggle />
</template>
}

View File

@ -1,16 +0,0 @@
import Component from "@glimmer/component";
import AiGistToggle from "../../components/ai-gist-toggle";
export default class AiTopicGist extends Component {
static shouldRender(outletArgs) {
return (
// "default" can be removed after the glimmer topic list is rolled out
(outletArgs?.name === "default" || outletArgs?.name === "topic.title") &&
!outletArgs.bulkSelectEnabled
);
}
<template>
<AiGistToggle />
</template>
}

View File

@ -1,12 +0,0 @@
import { tracked } from "@glimmer/tracking";
import Service from "@ember/service";
export default class GistPreference extends Service {
@tracked
preference = localStorage.getItem("aiGistPreference") || "gists_disabled";
setPreference(value) {
this.preference = value;
localStorage.setItem("aiGistPreference", value);
}
}

View File

@ -0,0 +1,29 @@
import { tracked } from "@glimmer/tracking";
import Service, { service } from "@ember/service";
export default class Gists extends Service {
@service router;
@tracked preference = localStorage.getItem("topicListLayout");
get shouldShow() {
const currentRoute = this.router.currentRoute.name;
const isDiscovery = currentRoute.includes("discovery");
const isNotCategories = !currentRoute.includes("categories");
const gistsAvailable =
this.router.currentRoute.attributes?.list?.topics?.some(
(topic) => topic.ai_topic_gist
);
return isDiscovery && isNotCategories && gistsAvailable;
}
setPreference(value) {
this.preference = value;
localStorage.setItem("topicListLayout", value);
if (this.preference === "table-ai") {
localStorage.setItem("aiPreferred", true);
}
}
}

View File

@ -0,0 +1,65 @@
.topic-list-layout-content {
.btn.--with-description {
display: grid;
grid-template-areas: "icon title" "icon description";
grid-template-columns: auto 1fr;
text-align: left;
.btn__description {
grid-area: description;
width: 100%;
font-size: var(--font-down-1);
color: var(--primary-high);
}
}
.btn:focus {
background: transparent;
}
.btn:focus-visible {
outline: 2px solid var(--tertiary);
background: transparent;
outline-offset: -2px;
}
.btn.--active {
background: var(--d-selected);
}
}
.topic-list-layout-table-ai {
.topic-list-item {
.link-bottom-line {
font-size: var(--font-down-1);
margin-top: 0.25em;
line-height: var(--line-height-medium);
}
.excerpt {
width: 100%;
line-height: var(--line-height-large);
margin-top: 0.15em;
margin-bottom: 0.15em;
max-width: 70ch;
}
&:not(.visited) {
.excerpt {
color: var(--primary-high);
}
}
}
.topic-excerpt {
display: none;
}
.mobile-view & {
.topic-list-item .excerpt {
margin-top: -0.25em;
margin-bottom: 0.25em;
}
.topic-item-stats .num.activity {
align-self: end;
margin-bottom: -0.15em; // vertical alignment
}
}
}

View File

@ -215,77 +215,3 @@
opacity: 1;
}
}
.ai-topic-gist {
width: 100%;
.mobile-view & {
position: relative;
top: -0.33em;
line-height: 1.4;
width: 90%;
}
&__text {
color: var(--primary-high);
text-wrap: pretty;
word-wrap: break-word;
font-size: var(--font-down-1);
max-width: 65ch;
.visited & {
color: var(--primary-medium);
}
}
}
.--topic-list-with-gist {
.topic-list-item {
.main-link {
line-height: var(--line-height-medium);
}
.link-bottom-line {
font-size: var(--font-down-1);
margin-top: 0.25em;
}
.ai-topic-gist {
font-size: var(--font-up-1);
line-height: var(--line-height-large);
margin-top: 0.25em;
margin-bottom: 0.15em;
}
.topic-post-badges {
font-size: var(--font-down-1);
}
}
.mobile-view & {
.topic-list {
.topic-list-item > .topic-list-data {
padding: 1em 0;
}
.topic-item-stats .num.activity {
align-self: end;
margin-bottom: -0.15em; // vertical alignment
}
.pull-left {
padding-top: 0.25em;
}
.right {
margin-left: 3.75em;
}
}
}
}
.ai-gists-dropdown-trigger {
font-size: var(--font-down-1);
color: var(--primary-medium);
padding-left: 0.25em;
.mobile-view & {
padding-left: 0;
color: var(--primary-high);
}
.d-icon {
color: var(--primary-low-mid);
margin-left: 0.15em;
}
}

View File

@ -481,9 +481,11 @@ en:
topic:
title: "Topic summary"
close: "Close summary panel"
gists_enabled: "with summary"
gists_enabled_long: "with AI-generated summary"
gists_disabled: "without summary"
topic_list_layout:
button:
compact: "Compact"
expanded: "Expanded"
expanded_description: "with AI summaries"
review:
types:
reviewable_ai_post:

View File

@ -24,6 +24,8 @@ register_asset "stylesheets/modules/summarization/mobile/ai-summary.scss", :mobi
register_asset "stylesheets/modules/summarization/common/ai-summary.scss"
register_asset "stylesheets/modules/summarization/desktop/ai-summary.scss", :desktop
register_asset "stylesheets/modules/summarization/common/ai-gists.scss"
register_asset "stylesheets/modules/ai-bot/common/bot-replies.scss"
register_asset "stylesheets/modules/ai-bot/common/ai-persona.scss"
register_asset "stylesheets/modules/ai-bot/mobile/ai-persona.scss", :mobile

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<!-- "Discourse Table" is a Discourse derivative of https://fontawesome.com/v6/icons/table-list?f=classic&s=solid -->
<symbol id="discourse-table" viewBox="0 0 512 512">
<g>
<g id="Layer_1">
<path d="M0,96C0,60.7,28.7,32,64,32h384c35.3,0,64,28.7,64,64v320c0,35.3-28.7,64-64,64H64c-35.3,0-64-28.7-64-64V96ZM448,96H64v64h384v-64ZM448,224H64v64h384v-64ZM448,352H64v64h384v-64Z"/>
</g>
</g>
</symbol>
<!-- "Discourse Table Sparkles" is a Discourse derivative of https://fontawesome.com/v6/icons/table-list?f=classic&s=solid -->
<symbol id="discourse-table-sparkles" viewBox="0 0 512 512">
<path d="M448 309.64V413H64V290h190c12.23 0 18.46-14.71 9.94-23.48-7.56-7.78-14.48-16.2-20.66-25.16-5.36-7.77-14.24-12.36-23.69-12.36H64V93h130c10.65 0 20.14-6.92 23.18-17.12a193.4 193.4 0 0 1 9.74-25.76c4.56-9.86-2.77-21.12-13.63-21.12H64C28.65 29 0 57.65 0 93v320c0 35.35 28.65 64 64 64h384c35.35 0 64-28.65 64-64V263.8c0-9.16-10.7-14.14-17.7-8.25a194.056 194.056 0 0 1-25.76 18.32c-12.66 7.53-20.53 21.05-20.53 35.77Z"/><path d="M247.46 122.15c-6.45 2.44-10.75 8.6-10.75 15.49s4.3 13.05 10.75 15.49l81.01 30.4 30.4 81.01c2.44 6.45 8.6 10.75 15.49 10.75s13.05-4.3 15.49-10.75l30.4-81.01 81.01-30.4c6.45-2.44 10.75-8.6 10.75-15.49s-4.3-13.05-10.75-15.49l-81.01-30.4-30.4-81.01c-2.44-6.45-8.6-10.75-15.49-10.75s-13.05 4.3-15.49 10.75l-30.4 81.01-81.01 30.4Z"/>
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB