Move API call to common package

This commit is contained in:
Brandon Thomas committed 2023-01-14 17:39:11 -05:00
1 parent ee0d4c883d
commit e42a1a5a73
5 files changed
+166 -121

No files matched your search

+10 -4
View File
@@ -38,7 +38,7 @@ import {
GetRetrievalJobStatus,
GetRetrievalJobStatusIsOk,
} from "@storyteller/components/src/api/retrieval/GetRetrievalJobStatus";
import { GetComputedTtsCategoryAssignmentsSuccessResponse } from "./v2/api/category/GetComputedTtsCategoryAssignments";
import { GetComputedTtsCategoryAssignmentsSuccessResponse } from "@storyteller/components/src/api/category/GetComputedTtsCategoryAssignments";
i18n
.use(initReactI18next) // passes i18n down to react-i18next
@@ -75,7 +75,9 @@ interface Props {
setAllTtsModels: (allTtsModels: TtsModelListItem[]) => void;
computedTtsCategoryAssignments?: GetComputedTtsCategoryAssignmentsSuccessResponse;
setComputedTtsCategoryAssignments: (categoryAssignments: GetComputedTtsCategoryAssignmentsSuccessResponse) => void;
setComputedTtsCategoryAssignments: (
categoryAssignments: GetComputedTtsCategoryAssignmentsSuccessResponse
) => void;
allTtsCategoriesByTokenMap: Map<string, TtsCategoryType>;
allTtsModelsByTokenMap: Map<string, TtsModelListItem>;
@@ -708,8 +710,12 @@ class App extends React.Component<Props, State> {
setTtsModels={this.props.setAllTtsModels}
allTtsCategories={this.props.allTtsCategories}
setAllTtsCategories={this.props.setAllTtsCategories}
computedTtsCategoryAssignments={this.props.computedTtsCategoryAssignments}
setComputedTtsCategoryAssignments={this.props.setComputedTtsCategoryAssignments}
computedTtsCategoryAssignments={
this.props.computedTtsCategoryAssignments
}
setComputedTtsCategoryAssignments={
this.props.setComputedTtsCategoryAssignments
}
allTtsCategoriesByTokenMap={
this.props.allTtsCategoriesByTokenMap
}
+121 -100
View File
@@ -1,19 +1,19 @@
import React, { useEffect, useState } from 'react';
import { TtsCategory } from './v2/api/category/ListTtsCategories';
import { TtsModelListItem } from '@storyteller/components/src/api/tts/ListTtsModels';
import { App } from './App';
import { v4 as uuidv4 } from 'uuid';
import { GetComputedTtsCategoryAssignmentsSuccessResponse } from './v2/api/category/GetComputedTtsCategoryAssignments';
import React, { useEffect, useState } from "react";
import { TtsCategory } from "./v2/api/category/ListTtsCategories";
import { TtsModelListItem } from "@storyteller/components/src/api/tts/ListTtsModels";
import { App } from "./App";
import { v4 as uuidv4 } from "uuid";
import { GetComputedTtsCategoryAssignmentsSuccessResponse } from "@storyteller/components/src/api/category/GetComputedTtsCategoryAssignments";
interface Props {
// Certan browsers (iPhone) have pitiful support for drawing APIs. Worse yet,
// they seem to lose the "touch event sandboxing" that allows for audio to be
// they seem to lose the "touch event sandboxing" that allows for audio to be
// played after user interaction if the XHRs delivering the audio don't do so
// as actual audio mimetypes. (Decoding from base64 and trying to play fails.)
enableSpectrograms: boolean,
enableSpectrograms: boolean;
// Whether or not to inform users that the name of the website has changed.
flashVocodesNotice: boolean,
flashVocodesNotice: boolean;
}
// Special synthetic categories created on the frontend.
@@ -26,7 +26,11 @@ export class SyntheticCategory {
ttsModels: Set<TtsModelListItem>;
constructor(name: string, token?: string, maybe_super_category_token?: string) {
constructor(
name: string,
token?: string,
maybe_super_category_token?: string
) {
this.name = name;
this.name_for_dropdown = name;
this.category_token = !!token ? token : `syn:${uuidv4()}`;
@@ -35,146 +39,163 @@ export class SyntheticCategory {
}
}
export type TtsCategoryType = TtsCategory|SyntheticCategory;
export type TtsCategoryType = TtsCategory | SyntheticCategory;
export function AppWrapper(props: Props) {
// Caches of all objects queried
// These may be triggered by a different page than the user initially lands on.
const [allTtsCategories, setAllTtsCategories] = useState<TtsCategoryType[]>([]);
const [allTtsCategories, setAllTtsCategories] = useState<TtsCategoryType[]>(
[]
);
const [allTtsModels, setAllTtsModels] = useState<TtsModelListItem[]>([]);
const [computedTtsCategoryAssignments, setComputedTtsCategoryAssignments] = useState<GetComputedTtsCategoryAssignmentsSuccessResponse|undefined>(undefined);
const [computedTtsCategoryAssignments, setComputedTtsCategoryAssignments] =
useState<GetComputedTtsCategoryAssignmentsSuccessResponse | undefined>(
undefined
);
// Precalculated maps for lookup by primary key
const [allCategoriesByTokenMap, setAllCategoriesByTokenMap] = useState<Map<string,TtsCategoryType>>(new Map());
const [allTtsModelsByTokenMap, setAllTtsModelsByTokenMap] = useState<Map<string,TtsModelListItem>>(new Map());
const [allCategoriesByTokenMap, setAllCategoriesByTokenMap] = useState<
Map<string, TtsCategoryType>
>(new Map());
const [allTtsModelsByTokenMap, setAllTtsModelsByTokenMap] = useState<
Map<string, TtsModelListItem>
>(new Map());
// Precalculated map for lookup by foreign key
// A TTS voice is attached to every category up the tree from the leaf.
// We recursively build this, 1) to ensure we can access a voice at all levels
// We recursively build this, 1) to ensure we can access a voice at all levels
// of specificity, and 2) to prune empty categories.
const [ttsModelsByCategoryToken, setTtsModelsByCategoryToken] = useState<Map<string,Set<TtsModelListItem>>>(new Map());
const [ttsModelsByCategoryToken, setTtsModelsByCategoryToken] = useState<
Map<string, Set<TtsModelListItem>>
>(new Map());
// Calculated dropdown options for every level of categories.
// Outer array has length of at least one, one element per <select>
// Inner array contains the categories in each level.
// Structure: [dropdownLevel][categories]
const [dropdownCategories, setDropdownCategories] = useState<TtsCategoryType[][]>([]);
const [dropdownCategories, setDropdownCategories] = useState<
TtsCategoryType[][]
>([]);
// User selections.
// Every category in the heirarchy that has been selected by the user.
// Empty list if none are selected.
// Structure: [firstSelected, secondSelected...]
const [selectedCategories, setSelectedCategories] = useState<TtsCategoryType[]>([]);
const [selectedCategories, setSelectedCategories] = useState<
TtsCategoryType[]
>([]);
// User selections.
const [maybeSelectedTtsModel, setMaybeSelectedTtsModel] = useState<TtsModelListItem|undefined>(undefined);
const [maybeSelectedTtsModel, setMaybeSelectedTtsModel] = useState<
TtsModelListItem | undefined
>(undefined);
// TODO: Handle empty category list
useEffect(() => {
// Category lookup by token
let categoriesByTokenMap = new Map();
allTtsCategories.forEach(category => {
allTtsCategories.forEach((category) => {
categoriesByTokenMap.set(category.category_token, category);
})
});
setAllCategoriesByTokenMap(categoriesByTokenMap);
// TTS model lookup by token
let ttsModelsByTokenMap = new Map();
allTtsModels.forEach(model => {
allTtsModels.forEach((model) => {
ttsModelsByTokenMap.set(model.model_token, model);
})
});
setAllTtsModelsByTokenMap(ttsModelsByTokenMap);
// Initial dropdown state
const rootCategories = allTtsCategories.filter(category => {
const rootCategories = allTtsCategories.filter((category) => {
return !category.maybe_super_category_token;
});
const rootLevel = [rootCategories];
setDropdownCategories(rootLevel);
/// NB: This was the really expensive cubic time++ aglorithm to recursively calculate nested category assignments.
/// This has since been ported to the server and will no longer be computed here. We're preserving this for posterity.
///
/// // Voice lookup table
/// let categoriesToTtsModelTokens = new Map();
/// // Category ancestry memoization
/// let categoryTokenToAllAncestorTokens : Map<string, Set<string>> = new Map();
///
/// // N * M with memoization should't be too bad here.
/// // Also note that the models should be lexographically sorted by name.
/// allTtsModels.forEach(ttsModel => {
/// if (ttsModel.category_tokens.length === 0) {
/// // TODO: Attach to "uncategorized" special category
/// return;
/// }
/// ttsModel.category_tokens.forEach(categoryToken => {
/// let ancestors = categoryTokenToAllAncestorTokens.get(categoryToken);
/// if (ancestors === undefined) {
/// ancestors = findAllAncestorTokens(categoryToken, categoriesByTokenMap);
/// categoryTokenToAllAncestorTokens.set(categoryToken, ancestors);
/// }
/// ancestors.forEach(categoryToken => {
/// let models : Set<TtsModelListItem> = categoriesToTtsModelTokens.get(categoryToken);
/// if (models === undefined) {
/// models = new Set();
/// categoriesToTtsModelTokens.set(categoryToken, models);
/// }
/// models.add(ttsModel);
/// })
/// });
/// });
/// setTtsModelsByCategoryToken(categoriesToTtsModelTokens);
/// NB: This was the really expensive cubic time++ aglorithm to recursively calculate nested category assignments.
/// This has since been ported to the server and will no longer be computed here. We're preserving this for posterity.
///
/// // Voice lookup table
/// let categoriesToTtsModelTokens = new Map();
/// // Category ancestry memoization
/// let categoryTokenToAllAncestorTokens : Map<string, Set<string>> = new Map();
///
/// // N * M with memoization should't be too bad here.
/// // Also note that the models should be lexographically sorted by name.
/// allTtsModels.forEach(ttsModel => {
/// if (ttsModel.category_tokens.length === 0) {
/// // TODO: Attach to "uncategorized" special category
/// return;
/// }
/// ttsModel.category_tokens.forEach(categoryToken => {
/// let ancestors = categoryTokenToAllAncestorTokens.get(categoryToken);
/// if (ancestors === undefined) {
/// ancestors = findAllAncestorTokens(categoryToken, categoriesByTokenMap);
/// categoryTokenToAllAncestorTokens.set(categoryToken, ancestors);
/// }
/// ancestors.forEach(categoryToken => {
/// let models : Set<TtsModelListItem> = categoriesToTtsModelTokens.get(categoryToken);
/// if (models === undefined) {
/// models = new Set();
/// categoriesToTtsModelTokens.set(categoryToken, models);
/// }
/// models.add(ttsModel);
/// })
/// });
/// });
/// setTtsModelsByCategoryToken(categoriesToTtsModelTokens);
const nestedCategoryTokenToModelTokensMap : Map<string, Set<string>> = computedTtsCategoryAssignments?.category_token_to_tts_model_tokens.recursive || new Map();
const nestedCategoryTokenToModelTokensMap: Map<
string,
Set<string>
> = computedTtsCategoryAssignments?.category_token_to_tts_model_tokens
.recursive || new Map();
let categoriesToTtsModelTokens = new Map();
nestedCategoryTokenToModelTokensMap.forEach((ttsModelTokens, categoryToken) => {
ttsModelTokens.forEach(ttsModelToken => {
let ttsModel = ttsModelsByTokenMap.get(ttsModelToken);
if (ttsModel === undefined) {
return;
}
let models : Set<TtsModelListItem> = categoriesToTtsModelTokens.get(categoryToken);
if (models === undefined) {
models = new Set();
categoriesToTtsModelTokens.set(categoryToken, models);
}
models.add(ttsModel);
})
})
nestedCategoryTokenToModelTokensMap.forEach(
(ttsModelTokens, categoryToken) => {
ttsModelTokens.forEach((ttsModelToken) => {
let ttsModel = ttsModelsByTokenMap.get(ttsModelToken);
if (ttsModel === undefined) {
return;
}
let models: Set<TtsModelListItem> =
categoriesToTtsModelTokens.get(categoryToken);
if (models === undefined) {
models = new Set();
categoriesToTtsModelTokens.set(categoryToken, models);
}
models.add(ttsModel);
});
}
);
setTtsModelsByCategoryToken(categoriesToTtsModelTokens);
}, [allTtsCategories, allTtsModels, computedTtsCategoryAssignments]);
return (
<App
enableSpectrograms={props.enableSpectrograms}
flashVocodesNotice={props.flashVocodesNotice}
allTtsCategories={allTtsCategories}
setAllTtsCategories={setAllTtsCategories}
allTtsModels={allTtsModels}
setAllTtsModels={setAllTtsModels}
computedTtsCategoryAssignments={computedTtsCategoryAssignments}
setComputedTtsCategoryAssignments={setComputedTtsCategoryAssignments}
allTtsCategoriesByTokenMap={allCategoriesByTokenMap}
allTtsModelsByTokenMap={allTtsModelsByTokenMap}
ttsModelsByCategoryToken={ttsModelsByCategoryToken}
dropdownCategories={dropdownCategories}
setDropdownCategories={setDropdownCategories}
selectedCategories={selectedCategories}
setSelectedCategories={setSelectedCategories}
maybeSelectedTtsModel={maybeSelectedTtsModel}
setMaybeSelectedTtsModel={setMaybeSelectedTtsModel}
/>
)
<App
enableSpectrograms={props.enableSpectrograms}
flashVocodesNotice={props.flashVocodesNotice}
allTtsCategories={allTtsCategories}
setAllTtsCategories={setAllTtsCategories}
allTtsModels={allTtsModels}
setAllTtsModels={setAllTtsModels}
computedTtsCategoryAssignments={computedTtsCategoryAssignments}
setComputedTtsCategoryAssignments={setComputedTtsCategoryAssignments}
allTtsCategoriesByTokenMap={allCategoriesByTokenMap}
allTtsModelsByTokenMap={allTtsModelsByTokenMap}
ttsModelsByCategoryToken={ttsModelsByCategoryToken}
dropdownCategories={dropdownCategories}
setDropdownCategories={setDropdownCategories}
selectedCategories={selectedCategories}
setSelectedCategories={setSelectedCategories}
maybeSelectedTtsModel={maybeSelectedTtsModel}
setMaybeSelectedTtsModel={setMaybeSelectedTtsModel}
/>
);
}
/// NB: This was the really expensive cubic time++ aglorithm to recursively calculate nested category assignments.
@@ -194,7 +215,7 @@ export function AppWrapper(props: Props) {
/// return [categoryToken];
/// }
/// return [
/// ...recursiveFindAllAncestorTokens(category.maybe_super_category_token, allCategoriesByTokenMap),
/// ...recursiveFindAllAncestorTokens(category.maybe_super_category_token, allCategoriesByTokenMap),
/// categoryToken,
/// ];
/// }
@@ -62,7 +62,7 @@ import { CheckoutSuccessPage } from "./premium/CheckoutSuccessPage";
import { CheckoutCancelPage } from "./premium/CheckoutCancelPage";
import { PortalSuccessPage } from "./premium/PortalSuccessPage";
import { PrivacyPage } from "./about/privacy_page/PrivacyPage";
import { GetComputedTtsCategoryAssignmentsSuccessResponse } from "../api/category/GetComputedTtsCategoryAssignments";
import { GetComputedTtsCategoryAssignmentsSuccessResponse } from "@storyteller/components/src/api/category/GetComputedTtsCategoryAssignments";
//import { LandingPage } from "./landing/LandingPage";
//import { VcModelListPage } from "./vc/vc_model_list/VcModelListPage";
@@ -114,9 +114,11 @@ interface Props {
allTtsCategories: TtsCategoryType[];
setAllTtsCategories: (allTtsCategories: TtsCategoryType[]) => void;
computedTtsCategoryAssignments?: GetComputedTtsCategoryAssignmentsSuccessResponse;
setComputedTtsCategoryAssignments: (categoryAssignments: GetComputedTtsCategoryAssignmentsSuccessResponse) => void;
setComputedTtsCategoryAssignments: (
categoryAssignments: GetComputedTtsCategoryAssignmentsSuccessResponse
) => void;
allTtsCategoriesByTokenMap: Map<string, TtsCategoryType>;
allTtsModelsByTokenMap: Map<string, TtsModelListItem>;
@@ -478,8 +480,12 @@ class PageContainer extends React.Component<Props, State> {
allTtsCategoriesByTokenMap={
this.props.allTtsCategoriesByTokenMap
}
computedTtsCategoryAssignments={this.props.computedTtsCategoryAssignments}
setComputedTtsCategoryAssignments={this.props.setComputedTtsCategoryAssignments}
computedTtsCategoryAssignments={
this.props.computedTtsCategoryAssignments
}
setComputedTtsCategoryAssignments={
this.props.setComputedTtsCategoryAssignments
}
allTtsModelsByTokenMap={this.props.allTtsModelsByTokenMap}
ttsModelsByCategoryToken={this.props.ttsModelsByCategoryToken}
dropdownCategories={this.props.dropdownCategories}
@@ -49,7 +49,12 @@ import { container, panel } from "../../../../data/animation";
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
import { TtsPageHero } from "./TtsPageHero";
import { Analytics } from "../../../../common/Analytics";
import { GetComputedTtsCategoryAssignments, GetComputedTtsCategoryAssignmentsIsError, GetComputedTtsCategoryAssignmentsIsOk, GetComputedTtsCategoryAssignmentsSuccessResponse } from "../../../api/category/GetComputedTtsCategoryAssignments";
import {
GetComputedTtsCategoryAssignments,
GetComputedTtsCategoryAssignmentsIsError,
GetComputedTtsCategoryAssignmentsIsOk,
GetComputedTtsCategoryAssignmentsSuccessResponse,
} from "@storyteller/components/src/api/category/GetComputedTtsCategoryAssignments";
export interface EnqueueJobResponsePayload {
success: boolean;
@@ -92,7 +97,9 @@ interface Props {
setAllTtsCategories: (allTtsCategories: TtsCategoryType[]) => void;
computedTtsCategoryAssignments?: GetComputedTtsCategoryAssignmentsSuccessResponse;
setComputedTtsCategoryAssignments: (categoryAssignments: GetComputedTtsCategoryAssignmentsSuccessResponse) => void;
setComputedTtsCategoryAssignments: (
categoryAssignments: GetComputedTtsCategoryAssignmentsSuccessResponse
) => void;
allTtsCategoriesByTokenMap: Map<string, TtsCategoryType>;
allTtsModelsByTokenMap: Map<string, TtsModelListItem>;
@@ -125,9 +132,10 @@ function TtsModelListPage(props: Props) {
const ttsModelsLoaded = ttsModels.length > 0;
const ttsCategoriesLoaded = allTtsCategories.length > 0;
const computedTtsCategoryAssignmentsLoaded =
computedTtsCategoryAssignments !== undefined &&
computedTtsCategoryAssignments.category_token_to_tts_model_tokens.recursive.size > 0;
const computedTtsCategoryAssignmentsLoaded =
computedTtsCategoryAssignments !== undefined &&
computedTtsCategoryAssignments.category_token_to_tts_model_tokens.recursive
.size > 0;
const listModels = useCallback(async () => {
if (ttsModelsLoaded) {
@@ -186,7 +194,7 @@ function TtsModelListPage(props: Props) {
useEffect(() => {
listModels();
listTtsCategories();
getComputedAssignments()
getComputedAssignments();
}, [listModels, listTtsCategories, getComputedAssignments]);
const handleChangeText = (ev: React.FormEvent<HTMLTextAreaElement>) => {
@@ -243,11 +251,13 @@ function TtsModelListPage(props: Props) {
let modelName = props.maybeSelectedTtsModel.title;
let userName = props.maybeSelectedTtsModel.creator_display_name;
directViewLink = (
<Link
to={modelLink}
onClick={() => { Analytics.ttsClickModelDetailsLink() } }
className="py-2"
>
<Link
to={modelLink}
onClick={() => {
Analytics.ttsClickModelDetailsLink();
}}
className="py-2"
>
<Trans i18nKey="tts.TtsModelListPage.form.modelSeeMoreLink">
See more details about the "
<span className="fw-semibold">{{ modelName }}</span>" model by&nbsp;
@@ -402,7 +412,9 @@ function TtsModelListPage(props: Props) {
<div className="text-input">
<textarea
onClick={() => { Analytics.ttsClickTextInputBox() } }
onClick={() => {
Analytics.ttsClickTextInputBox();
}}
onChange={handleChangeText}
className="form-control fs-5"
style={{ minHeight: "200px" }}