Merge pull request #164 from storytold/bflat-fy-explore-rollout

explore rollout on btn click
This commit is contained in:
Brandon Thomas authored and GitHub committed 2023-01-27 03:21:34 -05:00
commit b73aaab255
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">
@@ -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>
);
}
@@ -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>
</>
)
);
}