From b7fa2823f6bcc9f67ee44c4c560e3f578bbcb8a3 Mon Sep 17 00:00:00 2001 From: BFlat Date: Sun, 30 Apr 2023 15:09:26 +0700 Subject: [PATCH] moderation panel cleanup --- .../ModerationCategoryDeletePage.tsx | 119 +++-- .../ModerationTtsCategoryEditPage.tsx | 453 +++++++++++------- .../ModerationTtsCategoryListPage.tsx | 321 ++++++++----- .../ModerationPendingW2lTemplatesFc.tsx | 76 +-- .../ModerationUserList.tsx | 67 +-- .../ModerationVoiceStatsFc.tsx | 43 +- 6 files changed, 625 insertions(+), 454 deletions(-) diff --git a/packages/fakeyou.com/src/v2/view/pages/moderation/categories/ModerationCategoryDeletePage.tsx b/packages/fakeyou.com/src/v2/view/pages/moderation/categories/ModerationCategoryDeletePage.tsx index 393ad72f42..b7b78704da 100644 --- a/packages/fakeyou.com/src/v2/view/pages/moderation/categories/ModerationCategoryDeletePage.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/moderation/categories/ModerationCategoryDeletePage.tsx @@ -1,24 +1,35 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { BackLink } from '../../../_common/BackLink'; -import { Category, GetCategory, GetCategoryIsError, GetCategoryIsOk } from '@storyteller/components/src/api/category/GetCategory'; -import { WebUrl } from '../../../../../common/WebUrl'; -import { Link, useHistory } from 'react-router-dom'; -import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper'; -import { useParams } from 'react-router-dom'; -import { SetCategoryDeletionState, SetCategoryDeletionStateIsError, SetCategoryDeletionStateIsSuccess } from '@storyteller/components/src/api/moderation/category/SetCategoryDeletionState'; +import React, { useCallback, useEffect, useState } from "react"; +import { BackLink } from "../../../_common/BackLink"; +import { + Category, + GetCategory, + GetCategoryIsError, + GetCategoryIsOk, +} from "@storyteller/components/src/api/category/GetCategory"; +import { WebUrl } from "../../../../../common/WebUrl"; +import { Link, useHistory } from "react-router-dom"; +import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper"; +import { useParams } from "react-router-dom"; +import { + SetCategoryDeletionState, + SetCategoryDeletionStateIsError, + SetCategoryDeletionStateIsSuccess, +} from "@storyteller/components/src/api/moderation/category/SetCategoryDeletionState"; interface Props { - sessionWrapper: SessionWrapper, + sessionWrapper: SessionWrapper; } function ModerationCategoryDeletePage(props: Props) { - const { token } : { token : string } = useParams(); + const { token }: { token: string } = useParams(); const history = useHistory(); - const [category, setCategory] = useState(undefined); + const [category, setCategory] = useState(undefined); - const [errorMessage, setErrorMessage] = useState(undefined); + const [errorMessage, setErrorMessage] = useState( + undefined + ); const getCategory = useCallback(async (categoryToken: string) => { const categoryList = await GetCategory(categoryToken); @@ -26,11 +37,11 @@ function ModerationCategoryDeletePage(props: Props) { if (GetCategoryIsOk(categoryList)) { const category = categoryList.category; setCategory(category); - } else if (GetCategoryIsError(categoryList)) { + } else if (GetCategoryIsError(categoryList)) { setErrorMessage("error fetching category"); } }, []); - + useEffect(() => { getCategory(token); }, [token, getCategory]); @@ -40,31 +51,37 @@ function ModerationCategoryDeletePage(props: Props) { } if (category === undefined) { - return
+ return
; } const currentlyDeleted = !!category?.deleted_at; - const deletePageTitle = currentlyDeleted ? "Undelete Category?" : "Delete Category?"; - const deleteButtonTitle = currentlyDeleted ? "Confirm Undelete Category" : "Confirm Delete Category"; + const deletePageTitle = currentlyDeleted + ? "Undelete Category?" + : "Delete Category?"; + const deleteButtonTitle = currentlyDeleted + ? "Confirm Undelete Category" + : "Confirm Delete Category"; - const deleteButtonCss = currentlyDeleted ? - "button is-warning is-large is-fullwidth" : - "button is-danger is-large is-fullwidth"; + const deleteButtonCss = currentlyDeleted + ? "btn btn-destructive w-100" + : "btn btn-destructive w-100"; - const handleFormSubmit = async (ev: React.FormEvent) : Promise => { + const handleFormSubmit = async ( + ev: React.FormEvent + ): Promise => { ev.preventDefault(); let response = await SetCategoryDeletionState(token, !currentlyDeleted); if (SetCategoryDeletionStateIsError(response)) { - setErrorMessage('there was an error with the request'); // TODO: Fix error serialization + setErrorMessage("there was an error with the request"); // TODO: Fix error serialization } else if (SetCategoryDeletionStateIsSuccess(response)) { history.push(WebUrl.moderationTtsCategoryEdit(token)); } return false; - } + }; let errorFlash = <>; @@ -73,19 +90,20 @@ function ModerationCategoryDeletePage(props: Props) { <>
-
- {errorMessage} -
+
{errorMessage}
); } return ( -
-

{deletePageTitle}

+
+

{deletePageTitle}

- +
@@ -96,57 +114,54 @@ function ModerationCategoryDeletePage(props: Props) { - - + + - + - + - + - +
DetailValue + Detail + + Value +
Category Name - {category.name} - {category.name}
Creator - {category?.creator_username} + > + {category?.creator_username} +
Created On - {category.created_at} - {category.created_at}
Is Mod Approved? - {category.is_mod_approved ? "Yes" : "No"} - {category.is_mod_approved ? "Yes" : "No"}
Is Currently Deleted? - {currentlyDeleted ? "Yes" : "No"} - {currentlyDeleted ? "Yes" : "No"}
-
- + -
-
- - +
- ) + ); } export { ModerationCategoryDeletePage }; diff --git a/packages/fakeyou.com/src/v2/view/pages/moderation/categories/ModerationTtsCategoryEditPage.tsx b/packages/fakeyou.com/src/v2/view/pages/moderation/categories/ModerationTtsCategoryEditPage.tsx index b09651628d..cf83b82810 100644 --- a/packages/fakeyou.com/src/v2/view/pages/moderation/categories/ModerationTtsCategoryEditPage.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/moderation/categories/ModerationTtsCategoryEditPage.tsx @@ -1,39 +1,62 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { BackLink } from '../../../_common/BackLink'; -import { Category, GetCategory, GetCategoryIsError, GetCategoryIsOk } from '@storyteller/components/src/api/category/GetCategory'; -import { WebUrl } from '../../../../../common/WebUrl'; -import { Link, useHistory } from 'react-router-dom'; -import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper'; -import { useParams } from 'react-router-dom'; -import { EditCategory, EditCategoryIsError, EditCategoryIsSuccess, EditCategoryRequest } from '@storyteller/components/src/api/moderation/category/EditCategory'; -import { ListTtsCategories, ListTtsCategoriesIsError, ListTtsCategoriesIsOk, TtsCategory } from '@storyteller/components/src/api/category/ListTtsCategories'; -import { CategoryBreadcrumb } from '../../../_common/CategoryBreadcrumb'; +import React, { useCallback, useEffect, useState } from "react"; +import { BackLink } from "../../../_common/BackLink"; +import { + Category, + GetCategory, + GetCategoryIsError, + GetCategoryIsOk, +} from "@storyteller/components/src/api/category/GetCategory"; +import { WebUrl } from "../../../../../common/WebUrl"; +import { Link, useHistory } from "react-router-dom"; +import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper"; +import { useParams } from "react-router-dom"; +import { + EditCategory, + EditCategoryIsError, + EditCategoryIsSuccess, + EditCategoryRequest, +} from "@storyteller/components/src/api/moderation/category/EditCategory"; +import { + ListTtsCategories, + ListTtsCategoriesIsError, + ListTtsCategoriesIsOk, + TtsCategory, +} from "@storyteller/components/src/api/category/ListTtsCategories"; +import { CategoryBreadcrumb } from "../../../_common/CategoryBreadcrumb"; interface Props { - sessionWrapper: SessionWrapper, + sessionWrapper: SessionWrapper; } function ModerationTtsCategoryEditPage(props: Props) { - const { token } : { token : string } = useParams(); + const { token }: { token: string } = useParams(); const history = useHistory(); - const [category, setCategory] = useState(undefined); + const [category, setCategory] = useState(undefined); // Fields - const [name, setName] = useState(''); - const [maybeSuperCategoryToken, setMaybeSuperCategoryToken] = useState(undefined); // Optional - const [maybeDropdownName, setMaybeDropdownName] = useState(undefined); // Optional + const [name, setName] = useState(""); + const [maybeSuperCategoryToken, setMaybeSuperCategoryToken] = useState< + string | undefined + >(undefined); // Optional + const [maybeDropdownName, setMaybeDropdownName] = useState< + string | undefined + >(undefined); // Optional const [canDirectlyHaveModels, setCanDirectlyHaveModels] = useState(false); const [canHaveSubcategories, setCanHaveSubcategories] = useState(false); const [canOnlyModsApply, setCanOnlyModsApply] = useState(false); const [isModApproved, setIsModApproved] = useState(false); - const [maybeModComments, setMaybeModComments] = useState(undefined); // Optional + const [maybeModComments, setMaybeModComments] = useState( + undefined + ); // Optional // Additional object lookups to support parent categories const [allTtsCategories, setAllTtsCategories] = useState([]); - const [errorMessage, setErrorMessage] = useState(undefined); + const [errorMessage, setErrorMessage] = useState( + undefined + ); const getCategory = useCallback(async (categoryToken: string) => { const categoryList = await GetCategory(categoryToken); @@ -49,17 +72,17 @@ function ModerationTtsCategoryEditPage(props: Props) { setCanOnlyModsApply(category.can_only_mods_apply); setIsModApproved(category.is_mod_approved || false); // Default to false setMaybeModComments(category.maybe_mod_comments); - } else if (GetCategoryIsError(categoryList)) { + } else if (GetCategoryIsError(categoryList)) { setErrorMessage("error fetching category"); } }, []); - + const listTtsCategories = useCallback(async () => { const categoryList = await ListTtsCategories(); if (ListTtsCategoriesIsOk(categoryList)) { setAllTtsCategories(categoryList.categories); - } else if (ListTtsCategoriesIsError(categoryList)) { + } else if (ListTtsCategoriesIsError(categoryList)) { setErrorMessage("error listing all categories"); } }, []); @@ -74,52 +97,68 @@ function ModerationTtsCategoryEditPage(props: Props) { } if (category === undefined) { - return
+ return
; } const handleNameChange = (ev: React.FormEvent) => { const value = (ev.target as HTMLInputElement).value; setName(value); - } - - const handleMaybeDropdownNameChange = (ev: React.FormEvent) => { - let value = (ev.target as HTMLInputElement).value; - setMaybeDropdownName( !!value ? value : undefined); - } - - const handleCanDirectlyHaveModelsChange = (ev: React.FormEvent) => { - const value = (ev.target as HTMLInputElement).checked; - setCanDirectlyHaveModels(value); - } - - const handleCanHaveSubcategoriesChange = (ev: React.FormEvent) => { - const value = (ev.target as HTMLInputElement).checked; - setCanHaveSubcategories(value); - } - - const handleCanOnlyModsApplyChange = (ev: React.FormEvent) => { - const value = (ev.target as HTMLInputElement).checked; - setCanOnlyModsApply(value); - } - - const handleIsModApprovedChange = (ev: React.FormEvent) => { - const value = (ev.target as HTMLSelectElement).value; - const updatedValue = value === "true" ? true : false; - setIsModApproved(updatedValue) }; - const handleSetSuperCategory = async (ev: React.FormEvent) => { - const superCategoryToken = (ev.target as HTMLSelectElement).value; - let fixedSuperCategoryToken = !!superCategoryToken ? superCategoryToken : undefined; - setMaybeSuperCategoryToken(fixedSuperCategoryToken); - } + const handleMaybeDropdownNameChange = ( + ev: React.FormEvent + ) => { + let value = (ev.target as HTMLInputElement).value; + setMaybeDropdownName(!!value ? value : undefined); + }; - const handleFormSubmit = async (ev: React.FormEvent) : Promise => { + const handleCanDirectlyHaveModelsChange = ( + ev: React.FormEvent + ) => { + const value = (ev.target as HTMLInputElement).checked; + setCanDirectlyHaveModels(value); + }; + + const handleCanHaveSubcategoriesChange = ( + ev: React.FormEvent + ) => { + const value = (ev.target as HTMLInputElement).checked; + setCanHaveSubcategories(value); + }; + + const handleCanOnlyModsApplyChange = ( + ev: React.FormEvent + ) => { + const value = (ev.target as HTMLInputElement).checked; + setCanOnlyModsApply(value); + }; + + const handleIsModApprovedChange = ( + ev: React.FormEvent + ) => { + const value = (ev.target as HTMLSelectElement).value; + const updatedValue = value === "true" ? true : false; + setIsModApproved(updatedValue); + }; + + const handleSetSuperCategory = async ( + ev: React.FormEvent + ) => { + const superCategoryToken = (ev.target as HTMLSelectElement).value; + let fixedSuperCategoryToken = !!superCategoryToken + ? superCategoryToken + : undefined; + setMaybeSuperCategoryToken(fixedSuperCategoryToken); + }; + + const handleFormSubmit = async ( + ev: React.FormEvent + ): Promise => { ev.preventDefault(); setErrorMessage(undefined); - let request : EditCategoryRequest = { + let request: EditCategoryRequest = { name: name, maybe_dropdown_name: maybeDropdownName, maybe_mod_comments: maybeModComments, @@ -128,18 +167,18 @@ function ModerationTtsCategoryEditPage(props: Props) { can_have_subcategories: canHaveSubcategories, can_only_mods_apply: canOnlyModsApply, is_mod_approved: isModApproved, - } + }; const response = await EditCategory(token, request); if (EditCategoryIsError(response)) { - setErrorMessage('there was an error with the request'); // TODO: Fix error serialization + setErrorMessage("there was an error with the request"); // TODO: Fix error serialization } else if (EditCategoryIsSuccess(response)) { history.go(0); // NB: Force reload } return false; - } + }; let errorFlash = <>; @@ -148,38 +187,42 @@ function ModerationTtsCategoryEditPage(props: Props) { <>
-
- {errorMessage} -
+
{errorMessage}
); } const isModApprovedHtmlFormState = isModApproved ? "true" : "false"; - const maybeSuperCategoryTokenFormHtmlState = maybeSuperCategoryToken ? maybeSuperCategoryToken : ""; + const maybeSuperCategoryTokenFormHtmlState = maybeSuperCategoryToken + ? maybeSuperCategoryToken + : ""; - const superCategoryOptions = allTtsCategories.filter(category => { - const isSelf = token === category.category_token; - const cannotAdd = !category.can_have_subcategories; - return !isSelf && !cannotAdd; - }).map(category => { - return ( - <> - - - ) - }); + const superCategoryOptions = allTtsCategories + .filter((category) => { + const isSelf = token === category.category_token; + const cannotAdd = !category.can_have_subcategories; + return !isSelf && !cannotAdd; + }) + .map((category) => { + return ( + <> + + + ); + }); const currentlyDeleted = !!category?.deleted_at; - const deleteButtonTitle = currentlyDeleted ? "Undelete Category?" : "Delete Category?"; + const deleteButtonTitle = currentlyDeleted + ? "Undelete Category?" + : "Delete Category?"; - const deleteButtonCss = currentlyDeleted ? - "button is-warning is-large is-fullwidth" : - "button is-danger is-large is-fullwidth"; + const deleteButtonCss = currentlyDeleted + ? "btn btn-destructive w-100" + : "btn btn-destructive w-100"; let deletedNotice = <>; @@ -199,140 +242,184 @@ function ModerationTtsCategoryEditPage(props: Props) { const categoryHierarchy = recursiveBuildHierarchy(allTtsCategories, token); return ( -
-

Moderate TTS Category

+
+

Moderate TTS Category

- + -
-
- - -
+ {errorFlash} {deletedNotice} -
+
+
+ - - - - - -
- -
- -
-
- -
- -
- +
+
-
- -
- -
- -
-

(eg. if the category name is "Gender", this might be named "By Gender" for the dropdown.)

+
+ +
-
- - - - - -
-
- - - - - -
- - - -
-
- - - -
+
+
-
- -
+ +
+

+ (eg. if the category name is "Gender", this might be named "By + Gender" for the dropdown.) +

+
+ +
+ + +
+ + +
+ + + +
+ + +
+ +
+ +
-
+
+ - +
+ +
+
+
+ + + {deleteButtonTitle} + +
-
- - {deleteButtonTitle} - -
- - +
+ +
- ) + ); } // FIXME: This has been implemented three times, slightly differently -function recursiveBuildHierarchy(allCategories: TtsCategory[], currentToken: string): TtsCategory[] { - let found = allCategories.find(category => category.category_token === currentToken); +function recursiveBuildHierarchy( + allCategories: TtsCategory[], + currentToken: string +): TtsCategory[] { + let found = allCategories.find( + (category) => category.category_token === currentToken + ); if (found === undefined) { return []; } if (found.maybe_super_category_token === undefined) { return [found]; } - return [...recursiveBuildHierarchy(allCategories, found.maybe_super_category_token), found]; + return [ + ...recursiveBuildHierarchy(allCategories, found.maybe_super_category_token), + found, + ]; } export { ModerationTtsCategoryEditPage }; diff --git a/packages/fakeyou.com/src/v2/view/pages/moderation/categories/ModerationTtsCategoryListPage.tsx b/packages/fakeyou.com/src/v2/view/pages/moderation/categories/ModerationTtsCategoryListPage.tsx index f0c664c76e..f96223c013 100644 --- a/packages/fakeyou.com/src/v2/view/pages/moderation/categories/ModerationTtsCategoryListPage.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/moderation/categories/ModerationTtsCategoryListPage.tsx @@ -1,37 +1,53 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper'; -import { Gravatar } from '@storyteller/components/src/elements/Gravatar'; -import { WebUrl } from '../../../../../common/WebUrl'; -import { BackLink } from '../../../_common/BackLink'; -import { ListTtsCategoriesForModeration, ListTtsCategoriesForModerationIsError, ListTtsCategoriesForModerationIsOk, ListTtsCategoriesTriState, ModerationTtsCategory } from '@storyteller/components/src/api/moderation/category/ListTtsCategoriesForModeration'; -import { Link } from 'react-router-dom'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faBox, faBoxOpen, faLock } from '@fortawesome/free-solid-svg-icons'; -import { CategoryBreadcrumb } from '../../../_common/CategoryBreadcrumb'; +import React, { useCallback, useEffect, useState } from "react"; +import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper"; +// import { Gravatar } from "@storyteller/components/src/elements/Gravatar"; +import { WebUrl } from "../../../../../common/WebUrl"; +import { BackLink } from "../../../_common/BackLink"; +import { + ListTtsCategoriesForModeration, + ListTtsCategoriesForModerationIsError, + ListTtsCategoriesForModerationIsOk, + ListTtsCategoriesTriState, + ModerationTtsCategory, +} from "@storyteller/components/src/api/moderation/category/ListTtsCategoriesForModeration"; +import { Link } from "react-router-dom"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faBox, faBoxOpen, faLock } from "@fortawesome/free-solid-svg-icons"; +import { CategoryBreadcrumb } from "../../../_common/CategoryBreadcrumb"; interface Props { - sessionWrapper: SessionWrapper, + sessionWrapper: SessionWrapper; } interface SortableCategoryHierarchy { - hierarchy: ModerationTtsCategory[], + hierarchy: ModerationTtsCategory[]; // NB: While we could pull out the last item of the hierarchy list, this is more convenient. - category: ModerationTtsCategory, + category: ModerationTtsCategory; } function ModerationTtsCategoryListPage(props: Props) { - const [ttsCategories, setTtsCategories] = useState([]); - const [errorMessage, setErrorMessage] = useState(undefined); + const [ttsCategories, setTtsCategories] = useState( + [] + ); + const [errorMessage, setErrorMessage] = useState( + undefined + ); - const [deletedView, setDeletedView] = useState(ListTtsCategoriesTriState.Exclude); - const [unapprovedView, setUnapprovedView] = useState(ListTtsCategoriesTriState.Include); + const [deletedView, setDeletedView] = useState( + ListTtsCategoriesTriState.Exclude + ); + const [unapprovedView, setUnapprovedView] = + useState(ListTtsCategoriesTriState.Include); const listTtsCategories = useCallback(async () => { - const categoryList = await ListTtsCategoriesForModeration(deletedView, unapprovedView); + const categoryList = await ListTtsCategoriesForModeration( + deletedView, + unapprovedView + ); if (ListTtsCategoriesForModerationIsOk(categoryList)) { setTtsCategories(categoryList.categories); - } else if (ListTtsCategoriesForModerationIsError(categoryList)) { + } else if (ListTtsCategoriesForModerationIsError(categoryList)) { setErrorMessage("error listing all categories"); } }, [deletedView, unapprovedView]); @@ -50,7 +66,7 @@ function ModerationTtsCategoryListPage(props: Props) { if (maybeTriState !== undefined) { setDeletedView(maybeTriState); } - } + }; const handleUnapprovedChange = (ev: React.FormEvent) => { const value = (ev.target as HTMLInputElement).value; @@ -58,7 +74,7 @@ function ModerationTtsCategoryListPage(props: Props) { if (maybeTriState !== undefined) { setUnapprovedView(maybeTriState); } - } + }; let errorFlash = <>; @@ -66,80 +82,89 @@ function ModerationTtsCategoryListPage(props: Props) { errorFlash = ( <>
-
- {errorMessage} -
+
{errorMessage}
); } - let sortableHierarchies : SortableCategoryHierarchy[] = ttsCategories.map(category => { - const categoryHierarchy = recursiveBuildHierarchy(ttsCategories, category.category_token); - return { - hierarchy: categoryHierarchy, - category: category, - } - }).sort((first, second) => { - let maxLength = Math.max(first.hierarchy.length, second.hierarchy.length); - for (let i = 0; i < maxLength; i++) { - let f = first.hierarchy[i]; - let s = second.hierarchy[i]; - if (f !== undefined && s !== undefined) { - let compared = f.name.localeCompare(s.name) - if (compared !== 0) { - return compared; + let sortableHierarchies: SortableCategoryHierarchy[] = ttsCategories + .map((category) => { + const categoryHierarchy = recursiveBuildHierarchy( + ttsCategories, + category.category_token + ); + return { + hierarchy: categoryHierarchy, + category: category, + }; + }) + .sort((first, second) => { + let maxLength = Math.max(first.hierarchy.length, second.hierarchy.length); + for (let i = 0; i < maxLength; i++) { + let f = first.hierarchy[i]; + let s = second.hierarchy[i]; + if (f !== undefined && s !== undefined) { + let compared = f.name.localeCompare(s.name); + if (compared !== 0) { + return compared; + } + } else if (f === undefined) { + return -1; + } else if (s === undefined) { + return 1; } - } else if (f === undefined) { - return -1; - } else if (s === undefined) { - return 1; } - } - return -1; - }); + return -1; + }); return ( -
-

Moderate TTS Categories

+
+

Moderate TTS Categories

-
+ + Create new category + {errorFlash} -
-
Show Unapproved:  
@@ -148,36 +173,39 @@ function ModerationTtsCategoryListPage(props: Props) { Show Deleted:  

- + @@ -189,70 +217,98 @@ function ModerationTtsCategoryListPage(props: Props) { - {sortableHierarchies.map(sortableHierarchy => { + {sortableHierarchies.map((sortableHierarchy) => { const category = sortableHierarchy.category; const categoryHierarchy = sortableHierarchy.hierarchy; - let modOnlyIcon = category.can_only_mods_apply ? - <>  : - <>; + let modOnlyIcon = category.can_only_mods_apply ? ( + <> +   + + + ) : ( + <> + ); let parentCategoryIcon = <>; - + if (category.can_have_subcategories) { if (category.can_directly_have_models) { - parentCategoryIcon = <>  + parentCategoryIcon = ( + <> + +   + + ); } else { - parentCategoryIcon = <>  + parentCategoryIcon = ( + <> + +   + + ); } } let creatorLink = ; if (!!category?.creator_display_name) { - const creatorUrl = WebUrl.userProfilePage(category?.creator_username || "username error"); + const creatorUrl = WebUrl.userProfilePage( + category?.creator_username || "username error" + ); creatorLink = ( - + username={category.creator_display_name || ""} + email_hash={category.creator_gravatar_hash || ""} + /> */}   {category.creator_display_name} ); } - let approved = 'not set'; + let approved = "not set"; if (category.is_mod_approved === undefined) { - approved = 'not set'; + approved = "not set"; } else if (category.is_mod_approved === true) { - approved = 'approved'; + approved = "approved"; } else if (category.is_mod_approved === false) { - approved = 'DISAPPROVED'; + approved = "DISAPPROVED"; } - let deleted = !!category.deleted_at ? 'DELETED' : 'No'; + let deleted = !!category.deleted_at ? "DELETED" : "No"; return ( + + + - - - ); @@ -260,48 +316,49 @@ function ModerationTtsCategoryListPage(props: Props) {
{parentCategoryIcon} {modOnlyIcon} - + {creatorLink}{approved}{deleted} - {creatorLink} - - {approved} - - {deleted} - - edit + + edit +
-

- Note: Only approved, non-deleted categories will show up publicly. - New category suggestions by non-mods are "unapproved" by default. - Use "deletion" to hide categories you don't want to deal with anymore. +

+ Note: Only approved, non-deleted categories will show + up publicly. New category suggestions by non-mods are "unapproved" by + default. Use "deletion" to hide categories you don't want to deal with + anymore.

-
- - Create new category - -
-
- ) + ); } -function StringToTriState(state: string) : ListTtsCategoriesTriState | undefined { +function StringToTriState( + state: string +): ListTtsCategoriesTriState | undefined { switch (state) { - case 'include': + case "include": return ListTtsCategoriesTriState.Include; - case 'exclude': + case "exclude": return ListTtsCategoriesTriState.Exclude; - case 'only': + case "only": return ListTtsCategoriesTriState.Only; } } // FIXME: This has been implemented three times, slightly differently -function recursiveBuildHierarchy(allCategories: ModerationTtsCategory[], currentToken: string): ModerationTtsCategory[] { - let found = allCategories.find(category => category.category_token === currentToken); +function recursiveBuildHierarchy( + allCategories: ModerationTtsCategory[], + currentToken: string +): ModerationTtsCategory[] { + let found = allCategories.find( + (category) => category.category_token === currentToken + ); if (found === undefined) { return []; } if (found.maybe_super_category_token === undefined) { return [found]; } - return [...recursiveBuildHierarchy(allCategories, found.maybe_super_category_token), found]; + return [ + ...recursiveBuildHierarchy(allCategories, found.maybe_super_category_token), + found, + ]; } - export { ModerationTtsCategoryListPage }; diff --git a/packages/fakeyou.com/src/v2/view/pages/moderation/moderation_pending_w2l_templates/ModerationPendingW2lTemplatesFc.tsx b/packages/fakeyou.com/src/v2/view/pages/moderation/moderation_pending_w2l_templates/ModerationPendingW2lTemplatesFc.tsx index 73da968c99..94cfc1654c 100644 --- a/packages/fakeyou.com/src/v2/view/pages/moderation/moderation_pending_w2l_templates/ModerationPendingW2lTemplatesFc.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/moderation/moderation_pending_w2l_templates/ModerationPendingW2lTemplatesFc.tsx @@ -1,19 +1,25 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { Link } from 'react-router-dom'; -import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper'; -import { Gravatar } from '@storyteller/components/src/elements/Gravatar'; -import { formatDistance } from 'date-fns'; -import { WebUrl } from '../../../../../common/WebUrl'; -import { BackLink } from '../../../_common/BackLink'; -import { GetPendingW2lTemplates, GetPendingW2lTemplatesIsOk, PendingW2lTemplatesEntryForList } from '@storyteller/components/src/api/moderation/w2l/GetPendingW2lTemplates'; -import { PhotoVideoIcon } from '../../../_icons/PhotoVideoIcon'; +import React, { useCallback, useEffect, useState } from "react"; +import { Link } from "react-router-dom"; +import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper"; +import { Gravatar } from "@storyteller/components/src/elements/Gravatar"; +import { formatDistance } from "date-fns"; +import { WebUrl } from "../../../../../common/WebUrl"; +import { BackLink } from "../../../_common/BackLink"; +import { + GetPendingW2lTemplates, + GetPendingW2lTemplatesIsOk, + PendingW2lTemplatesEntryForList, +} from "@storyteller/components/src/api/moderation/w2l/GetPendingW2lTemplates"; +import { PhotoVideoIcon } from "../../../_icons/PhotoVideoIcon"; interface Props { - sessionWrapper: SessionWrapper, + sessionWrapper: SessionWrapper; } function ModerationPendingW2lTemplatesFc(props: Props) { - const [templates, setTemplates] = useState>([]); + const [templates, setTemplates] = useState< + Array + >([]); const getUsers = useCallback(async () => { const response = await GetPendingW2lTemplates(); @@ -21,16 +27,14 @@ function ModerationPendingW2lTemplatesFc(props: Props) { if (GetPendingW2lTemplatesIsOk(response)) { setTemplates(response.templates); } - }, []); useEffect(() => { getUsers(); }, [getUsers]); - if (!templates) { - return
+ return
; } if (!props.sessionWrapper.canApproveW2lTemplates()) { @@ -38,12 +42,14 @@ function ModerationPendingW2lTemplatesFc(props: Props) { } const now = new Date(); - let rows : Array = []; + let rows: Array = []; - templates.forEach(template=> { + templates.forEach((template) => { const createTime = new Date(template.created_at); - const relativeCreateTime = formatDistance(createTime, now, { addSuffix: true }); - + const relativeCreateTime = formatDistance(createTime, now, { + addSuffix: true, + }); + rows.push( @@ -55,7 +61,11 @@ function ModerationPendingW2lTemplatesFc(props: Props) { - +   {template.creator_display_name} @@ -63,12 +73,12 @@ function ModerationPendingW2lTemplatesFc(props: Props) { {template.template_type} {relativeCreateTime} - ) + ); }); return ( -
-

Unapproved W2L Templates

+
+

Unapproved W2L Templates

@@ -82,22 +92,22 @@ function ModerationPendingW2lTemplatesFc(props: Props) {

- Don't delete templates unless they overwhelmingly break the rules. - Users can use templates in private without them necessarily being approved for public use. + Don't delete templates unless they overwhelmingly break the rules. Users + can use templates in private without them necessarily being approved for + public use.


- Setting approved = TRUE will show the template on the main site for everyone to use. - Setting approved = FALSE will still let the author and people directly accessing via - URL to use the template, but will not make it public. - It will also remove it from the moderation queue. + Setting approved = TRUE will show the template on the main site for + everyone to use. Setting approved = FALSE will still let the author and + people directly accessing via URL to use the template, but will not make + it public. It will also remove it from the moderation queue.

-
- + @@ -107,12 +117,10 @@ function ModerationPendingW2lTemplatesFc(props: Props) { - - {rows} - + {rows}
Created At
- ) + ); } export { ModerationPendingW2lTemplatesFc }; diff --git a/packages/fakeyou.com/src/v2/view/pages/moderation/moderation_user_list/ModerationUserList.tsx b/packages/fakeyou.com/src/v2/view/pages/moderation/moderation_user_list/ModerationUserList.tsx index d740351f45..ee5e2b851b 100644 --- a/packages/fakeyou.com/src/v2/view/pages/moderation/moderation_user_list/ModerationUserList.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/moderation/moderation_user_list/ModerationUserList.tsx @@ -1,14 +1,18 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { Link } from 'react-router-dom'; -import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper'; -import { Gravatar } from '@storyteller/components/src/elements/Gravatar'; -import { GetUserList, GetUserListIsOk, UserForList } from '@storyteller/components/src/api/moderation/user/GetUserList'; -import { formatDistance } from 'date-fns'; -import { WebUrl } from '../../../../../common/WebUrl'; -import { BackLink } from '../../../_common/BackLink'; +import React, { useCallback, useEffect, useState } from "react"; +import { Link } from "react-router-dom"; +import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper"; +import { Gravatar } from "@storyteller/components/src/elements/Gravatar"; +import { + GetUserList, + GetUserListIsOk, + UserForList, +} from "@storyteller/components/src/api/moderation/user/GetUserList"; +import { formatDistance } from "date-fns"; +import { WebUrl } from "../../../../../common/WebUrl"; +import { BackLink } from "../../../_common/BackLink"; interface Props { - sessionWrapper: SessionWrapper, + sessionWrapper: SessionWrapper; } function ModerationUserListFc(props: Props) { @@ -20,16 +24,14 @@ function ModerationUserListFc(props: Props) { if (GetUserListIsOk(response)) { setUserList(response.users); } - }, []); useEffect(() => { getUsers(); }, [getUsers]); - if (!userList) { - return
+ return
; } if (!props.sessionWrapper.canBanUsers()) { @@ -37,21 +39,29 @@ function ModerationUserListFc(props: Props) { } const now = new Date(); - let rows : Array = []; + let rows: Array = []; - userList.forEach(user => { + userList.forEach((user) => { const createTime = new Date(user.created_at); - const relativeCreateTime = formatDistance(createTime, now, { addSuffix: true }); - + const relativeCreateTime = formatDistance(createTime, now, { + addSuffix: true, + }); + const updateTime = new Date(user.updated_at); - const relativeUpdateTime = formatDistance(updateTime, now, { addSuffix: true }); + const relativeUpdateTime = formatDistance(updateTime, now, { + addSuffix: true, + }); rows.push( {user.user_id} - +   {user.display_name} @@ -61,12 +71,12 @@ function ModerationUserListFc(props: Props) { {user.user_role_slug} {user.is_banned ? "banned" : ""} - ) + ); }); return ( -
-

User list

+
+

User list

@@ -74,14 +84,13 @@ function ModerationUserListFc(props: Props) {

- Brandon did the bare minimum to get this working. - It isn't paginated and will break once we get a lot of users. - It's also not sortable. - I'll need to cycle back and fix it after we launch. + Brandon did the bare minimum to get this working. It + isn't paginated and will break once we get a lot of users. It's also not + sortable. I'll need to cycle back and fix it after we launch.


- + @@ -93,12 +102,10 @@ function ModerationUserListFc(props: Props) { - - {rows} - + {rows}
Is Banned?
- ) + ); } export { ModerationUserListFc }; diff --git a/packages/fakeyou.com/src/v2/view/pages/moderation/moderation_voice_stats/ModerationVoiceStatsFc.tsx b/packages/fakeyou.com/src/v2/view/pages/moderation/moderation_voice_stats/ModerationVoiceStatsFc.tsx index b313f9dd97..fe196a3398 100644 --- a/packages/fakeyou.com/src/v2/view/pages/moderation/moderation_voice_stats/ModerationVoiceStatsFc.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/moderation/moderation_voice_stats/ModerationVoiceStatsFc.tsx @@ -1,11 +1,14 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper'; -import { WebUrl } from '../../../../../common/WebUrl'; -import { BackLink } from '../../../_common/BackLink'; -import { GetVoiceInventoryStats, GetVoiceInventoryStatsIsOk } from '@storyteller/components/src/api/moderation/stats/GetVoiceInventoryStats'; +import React, { useCallback, useEffect, useState } from "react"; +import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper"; +import { WebUrl } from "../../../../../common/WebUrl"; +import { BackLink } from "../../../_common/BackLink"; +import { + GetVoiceInventoryStats, + GetVoiceInventoryStatsIsOk, +} from "@storyteller/components/src/api/moderation/stats/GetVoiceInventoryStats"; interface Props { - sessionWrapper: SessionWrapper, + sessionWrapper: SessionWrapper; } function ModerationVoiceStatsFc(props: Props) { @@ -19,7 +22,7 @@ function ModerationVoiceStatsFc(props: Props) { setPublicVoicesCount(response.public_voices_count); } }, []); - + const reloadStats = useCallback(async () => { getVoiceStats(); }, [getVoiceStats]); @@ -28,9 +31,8 @@ function ModerationVoiceStatsFc(props: Props) { reloadStats(); }, [reloadStats]); - if (allVoicesCount === -1 && publicVoicesCount === -1) { - return
+ return
; } if (!props.sessionWrapper.canEditOtherUsersTtsModels()) { @@ -38,38 +40,33 @@ function ModerationVoiceStatsFc(props: Props) { } return ( -
-

Voice Stats

+
+

Voice Stats



- + - + - +
- Public voice vount - Public voice vount {publicVoicesCount} voices
- All voice count (incl banned) - All voice count (incl banned) {allVoicesCount} voices

- +
- ) + ); } export { ModerationVoiceStatsFc };