mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 17:58:55 +00:00
Merge pull request #164 from storytold/bflat-fy-explore-rollout
explore rollout on btn click
This commit is contained in:
4 files changed
+156
-160
No files matched your search
@@ -828,6 +828,23 @@ styles with important tags:
|
||||
.tippy-box[data-theme~="fakeyou"] > .tippy-svg-arrow {
|
||||
fill: lighten($secondary, 5%);
|
||||
}
|
||||
|
||||
.sliding-content {
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
transition-property: none !important;
|
||||
transition: all 0.1s !important;
|
||||
transition-timing-function: ease !important;
|
||||
}
|
||||
.sliding-content.open {
|
||||
overflow: visible;
|
||||
opacity: 1;
|
||||
height: 100%;
|
||||
transition-property: none !important;
|
||||
transition: all 0.4s !important;
|
||||
transition-timing-function: ease !important;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
|
||||
@@ -39,7 +39,8 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faArrowRight,
|
||||
faBarsStaggered,
|
||||
faCompass,
|
||||
faChevronDown,
|
||||
faChevronUp,
|
||||
faDeleteLeft,
|
||||
faGlobe,
|
||||
faVolumeHigh,
|
||||
@@ -64,11 +65,11 @@ import {
|
||||
AVAILABLE_TTS_LANGUAGE_CATEGORY_MAP,
|
||||
ENGLISH_LANGUAGE,
|
||||
} from "../../../../../_i18n/AvailableLanguageMap";
|
||||
import { ExploreVoicesModal } from "./explore/ExploreVoicesModal";
|
||||
import { WebUrl } from "../../../../../common/WebUrl";
|
||||
import { usePrefixedDocumentTitle } from "../../../../../common/UsePrefixedDocumentTitle";
|
||||
import { RatingButtons } from "../../../_common/ratings/RatingButtons";
|
||||
import { RatingStats } from "../../../_common/ratings/RatingStats";
|
||||
import { ExploreVoicesModal } from "./explore/ExploreVoicesModal";
|
||||
|
||||
export interface EnqueueJobResponsePayload {
|
||||
success: boolean;
|
||||
@@ -149,6 +150,12 @@ function TtsModelListPage(props: Props) {
|
||||
return () => clearTimeout(timeout);
|
||||
}, []);
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const handleClickExplore = () => {
|
||||
setIsOpen(!isOpen);
|
||||
};
|
||||
|
||||
let {
|
||||
setTtsModels,
|
||||
setAllTtsCategories,
|
||||
@@ -326,19 +333,19 @@ function TtsModelListPage(props: Props) {
|
||||
props.maybeSelectedTtsModel
|
||||
.ietf_primary_language_subtag as AvailableTtsLanguageKey
|
||||
] || ENGLISH_LANGUAGE;
|
||||
|
||||
|
||||
let ratingButtons = <></>;
|
||||
if (props.sessionWrapper.isLoggedIn()) {
|
||||
ratingButtons = (
|
||||
<RatingButtons
|
||||
entity_type="tts_model"
|
||||
entity_token={maybeSelectedTtsModel?.model_token || ""}
|
||||
<RatingButtons
|
||||
entity_type="tts_model"
|
||||
entity_token={maybeSelectedTtsModel?.model_token || ""}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
directViewLink = (
|
||||
<div className="d-flex flex-column direct-view-link zi-2 mb-4">
|
||||
<div className="d-flex flex-column direct-view-link mb-4">
|
||||
<div className="d-flex flex-column gap-3 flex-lg-row">
|
||||
<p className="flex-grow-1">
|
||||
{t("tts.TtsModelListPage.voiceDetails.voiceBy")}{" "}
|
||||
@@ -384,11 +391,14 @@ function TtsModelListPage(props: Props) {
|
||||
{ratingButtons}
|
||||
|
||||
<RatingStats
|
||||
positive_votes={maybeSelectedTtsModel?.user_ratings?.positive_count || 0}
|
||||
negative_votes={maybeSelectedTtsModel?.user_ratings?.negative_count || 0 }
|
||||
positive_votes={
|
||||
maybeSelectedTtsModel?.user_ratings?.positive_count || 0
|
||||
}
|
||||
negative_votes={
|
||||
maybeSelectedTtsModel?.user_ratings?.negative_count || 0
|
||||
}
|
||||
total_votes={maybeSelectedTtsModel?.user_ratings?.total_count || 0}
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -508,10 +518,37 @@ function TtsModelListPage(props: Props) {
|
||||
<div className="py-6">
|
||||
<div className="d-flex gap-4">
|
||||
<form
|
||||
className="w-100 d-flex flex-column gap-4"
|
||||
className="w-100 d-flex flex-column"
|
||||
onSubmit={handleFormSubmit}
|
||||
>
|
||||
<div>
|
||||
{/* Explore Rollout */}
|
||||
<div
|
||||
className={`sliding-content ${
|
||||
isOpen ? "open pb-4" : "closed"
|
||||
}`}
|
||||
>
|
||||
<ExploreVoicesModal
|
||||
allTtsCategories={props.allTtsCategories}
|
||||
allTtsModels={props.ttsModels}
|
||||
allTtsCategoriesByTokenMap={
|
||||
props.allTtsCategoriesByTokenMap
|
||||
}
|
||||
allTtsModelsByTokenMap={props.allTtsModelsByTokenMap}
|
||||
ttsModelsByCategoryToken={props.ttsModelsByCategoryToken}
|
||||
dropdownCategories={props.dropdownCategories}
|
||||
setDropdownCategories={props.setDropdownCategories}
|
||||
selectedCategories={props.selectedCategories}
|
||||
setSelectedCategories={props.setSelectedCategories}
|
||||
maybeSelectedTtsModel={props.maybeSelectedTtsModel}
|
||||
setMaybeSelectedTtsModel={props.setMaybeSelectedTtsModel}
|
||||
selectedTtsLanguageScope={props.selectedTtsLanguageScope}
|
||||
setSelectedTtsLanguageScope={
|
||||
props.setSelectedTtsLanguageScope
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="pb-4">
|
||||
<div className="d-flex gap-2">
|
||||
<label className="sub-title">
|
||||
{t("tts.TtsModelListPage.form.searchBarLabel")}
|
||||
@@ -521,7 +558,7 @@ function TtsModelListPage(props: Props) {
|
||||
</a>*/}
|
||||
</div>
|
||||
|
||||
<div className="d-flex flex-column flex-lg-row gap-3 ">
|
||||
<div className="d-flex flex-column flex-lg-row gap-3 zi-2">
|
||||
<div className="flex-grow-1">
|
||||
<SelectSearch
|
||||
allTtsCategories={props.allTtsCategories}
|
||||
@@ -541,16 +578,20 @@ function TtsModelListPage(props: Props) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{}
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
Analytics.ttsOpenExploreVoicesModal();
|
||||
}}
|
||||
onClick={handleClickExplore}
|
||||
// onClick={() => {
|
||||
// Analytics.ttsOpenExploreVoicesModal();
|
||||
// }}
|
||||
className="btn btn-primary rounded-50"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#exploreModal"
|
||||
type="button"
|
||||
>
|
||||
<FontAwesomeIcon icon={faCompass} className="me-2" />
|
||||
<FontAwesomeIcon
|
||||
icon={isOpen ? faChevronUp : faChevronDown}
|
||||
className="me-2"
|
||||
/>
|
||||
{t(
|
||||
"tts.TtsModelListPage.exploreModal.exploreModalOpenButton"
|
||||
)}
|
||||
@@ -558,25 +599,6 @@ function TtsModelListPage(props: Props) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Explore Modal */}
|
||||
<ExploreVoicesModal
|
||||
allTtsCategories={props.allTtsCategories}
|
||||
allTtsModels={props.ttsModels}
|
||||
allTtsCategoriesByTokenMap={props.allTtsCategoriesByTokenMap}
|
||||
allTtsModelsByTokenMap={props.allTtsModelsByTokenMap}
|
||||
ttsModelsByCategoryToken={props.ttsModelsByCategoryToken}
|
||||
dropdownCategories={props.dropdownCategories}
|
||||
setDropdownCategories={props.setDropdownCategories}
|
||||
selectedCategories={props.selectedCategories}
|
||||
setSelectedCategories={props.setSelectedCategories}
|
||||
maybeSelectedTtsModel={props.maybeSelectedTtsModel}
|
||||
setMaybeSelectedTtsModel={props.setMaybeSelectedTtsModel}
|
||||
selectedTtsLanguageScope={props.selectedTtsLanguageScope}
|
||||
setSelectedTtsLanguageScope={
|
||||
props.setSelectedTtsLanguageScope
|
||||
}
|
||||
/>
|
||||
|
||||
{directViewLink}
|
||||
|
||||
<div className="row gx-5 gy-5">
|
||||
|
||||
+54
-100
@@ -1,9 +1,5 @@
|
||||
import React from "react";
|
||||
import {
|
||||
faArrowRight,
|
||||
faCompass,
|
||||
faEraser,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faEraser } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { LanguageOptions } from "./LanguageOptions";
|
||||
import { TtsCategoryType } from "../../../../../../AppWrapper";
|
||||
@@ -90,108 +86,66 @@ export function ExploreVoicesModal(props: Props) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="modal fade"
|
||||
id="exploreModal"
|
||||
aria-labelledby="ModalLabel"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div className="modal-dialog modal-xl modal-fullscreen-lg-down modal-dialog-centered">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header p-3">
|
||||
<h5 className="modal-title fw-semibold" id="ModalLabel">
|
||||
<FontAwesomeIcon icon={faCompass} className="me-3" />
|
||||
{t("tts.TtsModelListPage.exploreModal.exploreModalTitle")}
|
||||
</h5>
|
||||
<div>
|
||||
<div className="row gx-3 gy-3">
|
||||
<div className="col-12 col-lg-3 input-icon-search">
|
||||
<label className="sub-title">
|
||||
{t("tts.TtsModelListPage.exploreModal.languageLabel")}
|
||||
</label>
|
||||
<LanguageOptions
|
||||
selectedTtsLanguageScope={props.selectedTtsLanguageScope}
|
||||
setSelectedTtsLanguageScope={props.setSelectedTtsLanguageScope}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-md-12 col-lg-9 input-icon-search">
|
||||
<div className="d-flex align-items-start">
|
||||
<label className="sub-title flex-grow-1">
|
||||
{t("tts.TtsModelListPage.exploreModal.categoryLabel")}
|
||||
</label>
|
||||
<button
|
||||
className="ms-3 fw-medium btn-link"
|
||||
onClick={() => {
|
||||
handleChangeCategory(0, "*");
|
||||
}}
|
||||
type="button"
|
||||
className="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
></button>
|
||||
</div>
|
||||
|
||||
<div className="modal-body p-3 p-lg-4">
|
||||
<div className="row gx-3 gy-3">
|
||||
<div className="col-12 col-lg-3 input-icon-search">
|
||||
<label className="sub-title">
|
||||
{t("tts.TtsModelListPage.exploreModal.languageLabel")}
|
||||
</label>
|
||||
<LanguageOptions
|
||||
selectedTtsLanguageScope={props.selectedTtsLanguageScope}
|
||||
setSelectedTtsLanguageScope={
|
||||
props.setSelectedTtsLanguageScope
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-md-12 col-lg-9 input-icon-search">
|
||||
<div className="d-flex align-items-start">
|
||||
<label className="sub-title flex-grow-1">
|
||||
{t("tts.TtsModelListPage.exploreModal.categoryLabel")}
|
||||
</label>
|
||||
<button
|
||||
className="ms-3 fw-medium btn-link"
|
||||
onClick={() => {
|
||||
handleChangeCategory(0, "*");
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={faEraser} className="me-2" />
|
||||
{t("tts.TtsModelListPage.exploreModal.clearCategoryFilterButton")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<CategoryOptions
|
||||
allTtsCategories={props.allTtsCategories}
|
||||
allTtsModels={props.allTtsModels}
|
||||
allTtsCategoriesByTokenMap={props.allTtsCategoriesByTokenMap}
|
||||
allTtsModelsByTokenMap={props.allTtsModelsByTokenMap}
|
||||
ttsModelsByCategoryToken={props.ttsModelsByCategoryToken}
|
||||
dropdownCategories={props.dropdownCategories}
|
||||
setDropdownCategories={props.setDropdownCategories}
|
||||
selectedCategories={props.selectedCategories}
|
||||
setSelectedCategories={props.setSelectedCategories}
|
||||
maybeSelectedTtsModel={props.maybeSelectedTtsModel}
|
||||
setMaybeSelectedTtsModel={props.setMaybeSelectedTtsModel}
|
||||
handleChangeCategory={handleChangeCategory}
|
||||
selectedTtsLanguageScope={props.selectedTtsLanguageScope}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<div className="row gx-3 gy-3">
|
||||
<ScopedVoiceModelOptions
|
||||
allTtsCategories={props.allTtsCategories}
|
||||
allTtsModels={props.allTtsModels}
|
||||
allTtsCategoriesByTokenMap={props.allTtsCategoriesByTokenMap}
|
||||
allTtsModelsByTokenMap={props.allTtsModelsByTokenMap}
|
||||
ttsModelsByCategoryToken={props.ttsModelsByCategoryToken}
|
||||
dropdownCategories={props.dropdownCategories}
|
||||
selectedCategories={props.selectedCategories}
|
||||
maybeSelectedTtsModel={props.maybeSelectedTtsModel}
|
||||
setMaybeSelectedTtsModel={props.setMaybeSelectedTtsModel}
|
||||
selectedTtsLanguageScope={props.selectedTtsLanguageScope}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<div className="p-4 w-100">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary w-100"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
>
|
||||
{t("tts.TtsModelListPage.exploreModal.useThisVoiceButton")}
|
||||
<FontAwesomeIcon icon={faArrowRight} className="ms-2" />
|
||||
<FontAwesomeIcon icon={faEraser} className="me-2" />
|
||||
{t("tts.TtsModelListPage.exploreModal.clearCategoryFilterButton")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<CategoryOptions
|
||||
allTtsCategories={props.allTtsCategories}
|
||||
allTtsModels={props.allTtsModels}
|
||||
allTtsCategoriesByTokenMap={props.allTtsCategoriesByTokenMap}
|
||||
allTtsModelsByTokenMap={props.allTtsModelsByTokenMap}
|
||||
ttsModelsByCategoryToken={props.ttsModelsByCategoryToken}
|
||||
dropdownCategories={props.dropdownCategories}
|
||||
setDropdownCategories={props.setDropdownCategories}
|
||||
selectedCategories={props.selectedCategories}
|
||||
setSelectedCategories={props.setSelectedCategories}
|
||||
maybeSelectedTtsModel={props.maybeSelectedTtsModel}
|
||||
setMaybeSelectedTtsModel={props.setMaybeSelectedTtsModel}
|
||||
handleChangeCategory={handleChangeCategory}
|
||||
selectedTtsLanguageScope={props.selectedTtsLanguageScope}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<ScopedVoiceModelOptions
|
||||
allTtsCategories={props.allTtsCategories}
|
||||
allTtsModels={props.allTtsModels}
|
||||
allTtsCategoriesByTokenMap={props.allTtsCategoriesByTokenMap}
|
||||
allTtsModelsByTokenMap={props.allTtsModelsByTokenMap}
|
||||
ttsModelsByCategoryToken={props.ttsModelsByCategoryToken}
|
||||
dropdownCategories={props.dropdownCategories}
|
||||
selectedCategories={props.selectedCategories}
|
||||
maybeSelectedTtsModel={props.maybeSelectedTtsModel}
|
||||
setMaybeSelectedTtsModel={props.setMaybeSelectedTtsModel}
|
||||
selectedTtsLanguageScope={props.selectedTtsLanguageScope}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+25
-22
@@ -1,8 +1,6 @@
|
||||
import React from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faMicrophone,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faMicrophone } from "@fortawesome/free-solid-svg-icons";
|
||||
import { TtsModelListItem } from "@storyteller/components/src/api/tts/ListTtsModels";
|
||||
import { TtsCategoryType } from "../../../../../../AppWrapper";
|
||||
import { Trans } from "react-i18next";
|
||||
@@ -25,7 +23,7 @@ interface Props {
|
||||
maybeSelectedTtsModel?: TtsModelListItem;
|
||||
setMaybeSelectedTtsModel: (maybeSelectedTtsModel: TtsModelListItem) => void;
|
||||
|
||||
selectedTtsLanguageScope: string,
|
||||
selectedTtsLanguageScope: string;
|
||||
}
|
||||
|
||||
export function ScopedVoiceModelOptions(props: Props) {
|
||||
@@ -44,17 +42,16 @@ export function ScopedVoiceModelOptions(props: Props) {
|
||||
if (maybeNewTtsModel !== undefined) {
|
||||
props.setMaybeSelectedTtsModel(maybeNewTtsModel);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const leafiestCategory = selectedCategories[selectedCategories.length - 1];
|
||||
|
||||
let leafiestCategoryModels: Array<TtsModelListItem> = [];
|
||||
|
||||
if (leafiestCategory !== undefined) {
|
||||
leafiestCategoryModels =
|
||||
Array.from(
|
||||
ttsModelsByCategoryToken.get(leafiestCategory.category_token) ||
|
||||
new Set());
|
||||
leafiestCategoryModels = Array.from(
|
||||
ttsModelsByCategoryToken.get(leafiestCategory.category_token) || new Set()
|
||||
);
|
||||
} else {
|
||||
leafiestCategoryModels = Array.from(new Set(allTtsModels));
|
||||
}
|
||||
@@ -65,16 +62,20 @@ export function ScopedVoiceModelOptions(props: Props) {
|
||||
if (props.selectedTtsLanguageScope === "*") {
|
||||
return true; // NB: Sentinel value of "*" means all languages.
|
||||
}
|
||||
return ttsModel.ietf_primary_language_subtag === props.selectedTtsLanguageScope;
|
||||
return (
|
||||
ttsModel.ietf_primary_language_subtag === props.selectedTtsLanguageScope
|
||||
);
|
||||
})
|
||||
.map((ttsModel) => {
|
||||
return {
|
||||
label: ttsModel.title,
|
||||
value: ttsModel.model_token,
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
let selectedOption = options.find((option) => option.value === maybeSelectedTtsModel?.model_token);
|
||||
let selectedOption = options.find(
|
||||
(option) => option.value === maybeSelectedTtsModel?.model_token
|
||||
);
|
||||
|
||||
if (selectedOption === undefined && options.length > 0) {
|
||||
// NB: We shouldn't select the first item in the list since that won't update the currently
|
||||
@@ -84,9 +85,9 @@ export function ScopedVoiceModelOptions(props: Props) {
|
||||
selectedOption = {
|
||||
label: "Select voice...",
|
||||
value: "*",
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
const voiceCount = options.length;
|
||||
|
||||
let isLoading = false;
|
||||
@@ -101,13 +102,13 @@ export function ScopedVoiceModelOptions(props: Props) {
|
||||
selectedOption = {
|
||||
label: "Loading...",
|
||||
value: "*",
|
||||
}
|
||||
};
|
||||
} else if (options.length === 0) {
|
||||
// NB: Perhaps the user has refined their search to be too narrow (langauge + category)
|
||||
selectedOption = {
|
||||
label: "No results (remove some filters)",
|
||||
value: "*",
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -132,18 +133,20 @@ export function ScopedVoiceModelOptions(props: Props) {
|
||||
options={options}
|
||||
classNames={SearchFieldClass}
|
||||
onChange={handleChange}
|
||||
onMenuOpen={() => { Analytics.ttsOpenScopedVoiceSelectMenu() } }
|
||||
onMenuOpen={() => {
|
||||
Analytics.ttsOpenScopedVoiceSelectMenu();
|
||||
}}
|
||||
isLoading={isLoading}
|
||||
// On mobile, we don't want the onscreen keyboard to take up half the UI.
|
||||
autoFocus={false}
|
||||
isSearchable={false}
|
||||
// NB: The following settings improve upon performance.
|
||||
// NB: The following settings improve upon performance.
|
||||
// See: https://github.com/JedWatson/react-select/issues/3128
|
||||
filterOption={createFilter({ignoreAccents: false})}
|
||||
components={{Option: FastReactSelectOption} as any}
|
||||
/>
|
||||
filterOption={createFilter({ ignoreAccents: false })}
|
||||
components={{ Option: FastReactSelectOption } as any}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user