diff --git a/_redirects b/_redirects index f137d71658..cc890efa66 100644 --- a/_redirects +++ b/_redirects @@ -10,7 +10,7 @@ /coming-soon* /index.html 200 /commissions* /index.html 200 /contribute* /index.html 200 -/face-animation* /index.html 200 +/face-animator* /index.html 200 /firehose* /index.html 200 /guide* /index.html 200 /jobs* /index.html 200 @@ -18,6 +18,7 @@ /landing_temp* /index.html 200 /leaderboard* /index.html 200 /login* /index.html 200 +/media* /index.html 200 /moderation* /index.html 200 /news* /index.html 200 /obs_configs* /index.html 200 @@ -40,6 +41,10 @@ /video* /index.html 200 /voice-conversion* /index.html 200 /w2l* /index.html 200 + +# Old paths +/face-animation* /face-animator 302 + # This is for YCombinator. # This is the *OLD* video for Summer 2022. We'll re-edit. # PLEASE DO NOT ACECSS THIS URL OR WATCH. IT WILL CHANGE THE diff --git a/packages/components/src/api/ApiConfig.ts b/packages/components/src/api/ApiConfig.ts index 459efd25f2..649be9f3f4 100644 --- a/packages/components/src/api/ApiConfig.ts +++ b/packages/components/src/api/ApiConfig.ts @@ -100,15 +100,15 @@ class ApiConfig { } login(): string { - return `${this.getApiOrigin()}/login`; + return `${this.getApiOrigin()}/v1/login`; } logout(): string { - return `${this.getApiOrigin()}/logout`; + return `${this.getApiOrigin()}/v1/logout`; } sessionDetails(): string { - return `${this.getApiOrigin()}/session`; + return `${this.getApiOrigin()}/v1/session`; } getQueueStats(): string { @@ -410,6 +410,12 @@ class ApiConfig { return `${this.getApiOrigin()}/voice_clone_requests/check`; } + // =============== Media Files =============== + + getMediaFile(mediaFileToken: string): string { + return `${this.getApiOrigin()}/v1/media_files/file/${mediaFileToken}`; + } + // =============== File Uploads =============== uploadAudio(): string { diff --git a/packages/components/src/api/_common/UserDetailsLight.ts b/packages/components/src/api/_common/UserDetailsLight.ts new file mode 100644 index 0000000000..a913fe44df --- /dev/null +++ b/packages/components/src/api/_common/UserDetailsLight.ts @@ -0,0 +1,17 @@ +/// A common type returned by several endpoints. +/// Basic information to display a user and their avatar. +export interface UserDetailsLight { + user_token: string, + /// Username (lowercase) + username: string, + /// Username with user-specified capitalization + display_name: string, + gravatar_hash: string, + default_avatar: DefaultAvatarInfo, + +} + +export interface DefaultAvatarInfo { + image_index: number, + color_index: number, +} diff --git a/packages/components/src/api/_common/enums/MediaFileType.ts b/packages/components/src/api/_common/enums/MediaFileType.ts new file mode 100644 index 0000000000..b104aeaa7b --- /dev/null +++ b/packages/components/src/api/_common/enums/MediaFileType.ts @@ -0,0 +1,6 @@ + +export enum MediaFileType { + Audio = "audio", + Video = "video", + Image = "image", +} diff --git a/packages/components/src/api/media_files/GetMediaFile.ts b/packages/components/src/api/media_files/GetMediaFile.ts new file mode 100644 index 0000000000..29bdc1a7fc --- /dev/null +++ b/packages/components/src/api/media_files/GetMediaFile.ts @@ -0,0 +1,52 @@ +import { ApiConfig } from "../ApiConfig"; +import { UserDetailsLight } from "../_common/UserDetailsLight"; +import { MediaFileType } from "../_common/enums/MediaFileType"; + +export interface MediaFile { + token: string, + media_type: MediaFileType, + public_bucket_path: string, + maybe_creator_user: UserDetailsLight | null, + creator_set_visibility: string, + created_at: Date, + updated_at: Date, +} + +export enum MediaFileLookupError { + NotFound, + ServerError, + FrontendError, +} + +export interface GetMediaFileResponse { + success: boolean, + media_file?: MediaFile, +} + +export async function GetMediaFile(mediaFileToken: string) : Promise { + const endpoint = new ApiConfig().getMediaFile(mediaFileToken); + + return await fetch(endpoint, { + method: 'GET', + headers: { + 'Accept': 'application/json', + }, + credentials: 'include', + }) + .then(res => res.json()) + .then(res => { + const response : GetMediaFileResponse = res; + + if (response && response.success && response.media_file) { + // NB: Timestamps aren't converted to Date objects on their own! + response.media_file.created_at = new Date(response.media_file.created_at); + response.media_file.updated_at = new Date(response.media_file.updated_at); + return response; + } else { + return { success : false }; + } + }) + .catch(e => { + return { success : false }; + }); +} diff --git a/packages/components/src/api/model_inference/GetModelInferenceJobStatus.ts b/packages/components/src/api/model_inference/GetModelInferenceJobStatus.ts index 2da780cdef..3610bcf052 100644 --- a/packages/components/src/api/model_inference/GetModelInferenceJobStatus.ts +++ b/packages/components/src/api/model_inference/GetModelInferenceJobStatus.ts @@ -30,6 +30,7 @@ export interface RequestDetails { export interface StatusDetails { status: string, maybe_extra_status_description?: string, + maybe_failure_category?: string, attempt_count: number, } diff --git a/packages/components/src/jobs/InferenceJob.ts b/packages/components/src/jobs/InferenceJob.ts index 586126aa83..638530ee56 100644 --- a/packages/components/src/jobs/InferenceJob.ts +++ b/packages/components/src/jobs/InferenceJob.ts @@ -31,6 +31,7 @@ export class InferenceJob { maybeResultType: string | undefined | null; maybeResultToken: string | undefined | null; maybeResultPublicBucketMediaPath: string | undefined | null; + maybeFailureCategory: string | undefined | null; constructor( // PK @@ -50,6 +51,7 @@ export class InferenceJob { maybeResultEntityType: string | undefined | null = null, maybeResultEntityToken: string | undefined | null = null, maybeResultPublicBucketMediaPath: string | undefined | null = null, + maybeFailureCategory: string | undefined | null = null ) { this.jobToken = jobToken; this.frontendJobType = frontendJobType; @@ -77,6 +79,10 @@ export class InferenceJob { } this.maybeResultPublicBucketMediaPath = maybeResultPublicBucketMediaPath; + + if (!!maybeFailureCategory) { + this.maybeFailureCategory = maybeFailureCategory; + } } static fromResponse(response: ModelInferenceJobStatus, frontendJobType: FrontendInferenceJobType) : InferenceJob { @@ -93,6 +99,7 @@ export class InferenceJob { response.maybe_result?.entity_type, response.maybe_result?.entity_token, response.maybe_result?.maybe_public_bucket_media_path, + response.status.maybe_failure_category ); } } diff --git a/packages/fakeyou.com/package.json b/packages/fakeyou.com/package.json index bfe2d95b64..e9fd981692 100644 --- a/packages/fakeyou.com/package.json +++ b/packages/fakeyou.com/package.json @@ -31,9 +31,10 @@ "@types/react": "^16.9.36", "@types/react-dom": "^16.9.8", "axios": "^0.21.0", + "bootstrap": "5.1.3", "bulma": "^0.9.0", "craco": "^0.0.3", - "date-fns": "^2.22.1", + "date-fns": "2.22.1", "framer-motion": "^6.5.1", "howler": "^2.2.0", "i18next": "^21.6.11", diff --git a/packages/fakeyou.com/public/locales/en/FaceAnimator.json b/packages/fakeyou.com/public/locales/en/FaceAnimator.json index f1bbaeacda..c47dc54e43 100644 --- a/packages/fakeyou.com/public/locales/en/FaceAnimator.json +++ b/packages/fakeyou.com/public/locales/en/FaceAnimator.json @@ -3,12 +3,14 @@ "audio": "Audio", "image": "Image", "subtitle": "Select and image with a clear face, and an audio sample, and generate a lipsynced video.", - "title": "Face Animator" + "title": "Face Animator", + "yourJobs": "Your jobs" }, "inputs": { "downloadFile": "Download File", "generate": "Generate", - "makeAnother": "Make another" + "makeAnother": "Make another", + "viewResult": "View result" }, "status": { "animationComplete": "Animation complete!", diff --git a/packages/fakeyou.com/src/App.tsx b/packages/fakeyou.com/src/App.tsx index 2da2b00a82..e9216c3325 100644 --- a/packages/fakeyou.com/src/App.tsx +++ b/packages/fakeyou.com/src/App.tsx @@ -1,4 +1,5 @@ import "./AppNew.scss"; +import "scss/custom-bootstrap.scss"; import React from "react"; import { ApiConfig } from "@storyteller/components"; @@ -57,6 +58,8 @@ import { VoiceConversionModelUploadJob } from "@storyteller/components/src/jobs/ import { VoiceConversionModelListItem } from "@storyteller/components/src/api/voice_conversion/ListVoiceConversionModels"; import HttpBackend from "i18next-http-backend"; +import { InferenceJobs } from "components/providers"; + // NB: We're transitioning over to this instance of i18n-next that loads translations over HTTP from Json Files. // The old i18n-next instance (see below) bakes in translations into the compiled javascript blob. // This new instance uses the Locize paid service to manage translation strings on their website. It's automated, @@ -182,12 +185,15 @@ function newVocodes() { return discord || twitter || alphaCookie; } -function initInferenceJobsByCategoryMap() : Map { - let inferenceJobsByCategory = new Map(); - inferenceJobsByCategory.set(FrontendInferenceJobType.FaceAnimation, []); - inferenceJobsByCategory.set(FrontendInferenceJobType.TextToSpeech, []); - inferenceJobsByCategory.set(FrontendInferenceJobType.VoiceConversion, []); - return inferenceJobsByCategory; +function initInferenceJobsByCategoryMap(): Map< + FrontendInferenceJobType, + InferenceJob[] +> { + let inferenceJobsByCategory = new Map(); + inferenceJobsByCategory.set(FrontendInferenceJobType.FaceAnimation, []); + inferenceJobsByCategory.set(FrontendInferenceJobType.TextToSpeech, []); + inferenceJobsByCategory.set(FrontendInferenceJobType.VoiceConversion, []); + return inferenceJobsByCategory; } function isMacOs() { @@ -779,6 +785,7 @@ class App extends React.Component { */}
+ { /> +
diff --git a/packages/fakeyou.com/src/components/common/Accordion/Accordion.scss b/packages/fakeyou.com/src/components/common/Accordion/Accordion.scss new file mode 100644 index 0000000000..803aeb60d5 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/Accordion/Accordion.scss @@ -0,0 +1,45 @@ +@import "scss/colours.scss"; + +.fy-accordion-item { + border: 1px solid rgba($white, 0.12); + border-radius: 0.5rem; + overflow: hidden; +} + +.fy-accordion-header { + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + background-color: $secondary; + transition: all 0.2s ease; + font-weight: 500; + + &.open { + background-color: lighten($secondary, 5%); + } +} + +.fy-accordion-content { + overflow: hidden; + background-color: lighten($panel, 4%); + + &.open { + opacity: 1; + max-height: 100%; + visibility: visible; + } +} + +.fy-accordion-content table { + width: 100%; +} + +.caret-icon { + transition: transform 0.2s ease; + transform-origin: center; +} + +.caret-rotated { + transform: rotate(180deg); +} diff --git a/packages/fakeyou.com/src/components/common/Accordion/Accordion.tsx b/packages/fakeyou.com/src/components/common/Accordion/Accordion.tsx new file mode 100644 index 0000000000..4eceaeee37 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/Accordion/Accordion.tsx @@ -0,0 +1,119 @@ +import React, { + useState, + useRef, + useEffect, + createContext, + useContext, +} from "react"; +import "./Accordion.scss"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faChevronDown } from "@fortawesome/pro-solid-svg-icons"; +import { animated, useSpring } from "@react-spring/web"; + +interface AccordionProps { + children: React.ReactNode; +} + +interface AccordionItemProps { + title: string; + defaultOpen?: boolean; + children: React.ReactNode; +} + +interface AccordionContextType { + openItems: string[]; + toggleItem: (title: string) => void; +} + +const AccordionContext = createContext( + undefined +); + +function AccordionItem({ + title, + defaultOpen = false, + children, +}: AccordionItemProps) { + const { openItems, toggleItem } = useContext( + AccordionContext + ) as AccordionContextType; + const isOpen = openItems.includes(title); + + const [contentHeight, setContentHeight] = useState( + isOpen ? undefined : 0 + ); + const contentRef = useRef(null); + + useEffect(() => { + if (contentRef.current) { + setContentHeight(isOpen ? contentRef.current.scrollHeight : 0); + } + }, [isOpen]); + + const heightProps = useSpring({ + height: isOpen ? `${contentHeight}px` : "0px", + config: { tension: 300, friction: 25, clamp: true }, + }); + + const contentOpacityProps = useSpring({ + opacity: isOpen ? 1 : 0, + config: { tension: 300, friction: 25, clamp: true }, + }); + + const handleClick = () => { + toggleItem(title); + }; + + return ( +
+
+ {title} + + + +
+ + + {children} + + +
+ ); +} + +function Accordion({ children }: AccordionProps) { + const [openItems, setOpenItems] = useState([]); + + const toggleItem = (title: string) => { + if (openItems.includes(title)) { + setOpenItems(openItems.filter((item) => item !== title)); + } else { + setOpenItems([...openItems, title]); + } + }; + + // Initialize openItems based on defaultOpen props of AccordionItem children + useEffect(() => { + const defaultOpenItems = React.Children.toArray(children) + .filter((child: any) => child.props.defaultOpen) + .map((child: any) => child.props.title); + setOpenItems(defaultOpenItems); + }, [children]); + + return ( + +
{children}
+
+ ); +} + +Accordion.Item = AccordionItem; + +export default Accordion; diff --git a/packages/fakeyou.com/src/components/common/Accordion/index.ts b/packages/fakeyou.com/src/components/common/Accordion/index.ts new file mode 100644 index 0000000000..366853b1f2 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/Accordion/index.ts @@ -0,0 +1 @@ +export { default } from "./Accordion"; diff --git a/packages/fakeyou.com/src/components/common/Button/Button.scss b/packages/fakeyou.com/src/components/common/Button/Button.scss index d5a58e8c67..17ba047172 100644 --- a/packages/fakeyou.com/src/components/common/Button/Button.scss +++ b/packages/fakeyou.com/src/components/common/Button/Button.scss @@ -3,13 +3,24 @@ .button { border-radius: 0.5rem; outline: none; - font-size: 15px; - font-weight: 500; + font-size: 15px !important; + font-weight: 500 !important; padding: 10px 20px; z-index: 2; display: flex; align-items: center; justify-content: center; + color: $white !important; + + &-square { + width: 46.5px; + height: 46.5px; + } + + &-square-small { + width: 37.5px; + height: 37.5px; + } } .button-primary, @@ -19,19 +30,19 @@ border: 2.5px solid; border-color: $primary; transition: 0.2s; - color: $white; + color: $white !important; } .button-primary:hover { background-color: lighten($primary, 5%); border-color: lighten($primary, 5%); - color: $white; + color: $white !important; } .button-secondary, .button-secondary:focus, .button-secondary:active { background-color: $secondary; - color: $white; + color: $white !important; border: 2.5px solid; border-color: $secondary; transition: 0.2s; @@ -39,24 +50,24 @@ .button-secondary:hover { background-color: lighten($secondary, 5%); - color: $white; + color: $white !important; border: 2.5px solid; border-color: lighten($secondary, 5%); } -.button-destructive, -.button-destructive:focus, -.button-destructive:active { +.button-danger, +.button-danger:focus, +.button-danger:active { background-color: $danger; - color: $white; + color: $white !important; border: 2.5px solid; border-color: $danger; transition: 0.2s; } -.button-destructive:hover { +.button-danger:hover { background-color: lighten($danger, 5%); - color: $white; + color: $white !important; border: 2.5px solid; border-color: lighten($danger, 5%); } @@ -65,7 +76,7 @@ .button-small:focus, .button-small:active { font-weight: 500; - font-size: 14px; + font-size: 14px !important; padding: 8.25px 16px; transition: 0.2s; border: none; diff --git a/packages/fakeyou.com/src/components/common/Button/Button.tsx b/packages/fakeyou.com/src/components/common/Button/Button.tsx index 188ca25bac..2aaafcd5b3 100644 --- a/packages/fakeyou.com/src/components/common/Button/Button.tsx +++ b/packages/fakeyou.com/src/components/common/Button/Button.tsx @@ -1,37 +1,97 @@ import React, { ButtonHTMLAttributes } from "react"; +import { Link } from "react-router-dom"; import "./Button.scss"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { IconDefinition } from "@fortawesome/fontawesome-svg-core"; +import Tippy from "@tippyjs/react"; +import "tippy.js/dist/tippy.css"; interface ButtonProps extends ButtonHTMLAttributes { - label?: string; + label?: string | null; icon?: IconDefinition; small?: boolean; - secondary?: boolean; - danger?: boolean; + variant?: "primary" | "secondary" | "danger"; + to?: string; + href?: string; + target?: "_blank" | "_self"; + square?: boolean; + tooltip?: string; + full?: boolean; + iconFlip?: boolean; + download?: boolean | string; } export default function Button({ label, icon, small, - secondary, - danger, + variant = "primary", + to, + href, + target = "_self", + square, + tooltip, + full = false, + iconFlip = false, + download, ...rest }: ButtonProps) { - const buttonClasses = `button ${small ? "button-small" : ""} - ${ - secondary - ? "button-secondary" - : danger - ? "button-destructive" - : "button-primary" - } `; + let iconMarginClass = !square && label ? (iconFlip ? "ms-2" : "me-2") : ""; + let IconComponent = icon ? ( + + ) : null; - return ( - ); + + return tooltip ? ( + + {WrappedButton} + + ) : ( + WrappedButton + ); } diff --git a/packages/fakeyou.com/src/components/common/Button/index.tsx b/packages/fakeyou.com/src/components/common/Button/index.tsx new file mode 100644 index 0000000000..c4719be7c0 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/Button/index.tsx @@ -0,0 +1 @@ +export { default } from "./Button"; diff --git a/packages/fakeyou.com/src/components/common/CheckableTag/CheckableTag.scss b/packages/fakeyou.com/src/components/common/CheckableTag/CheckableTag.scss new file mode 100644 index 0000000000..8274c937bf --- /dev/null +++ b/packages/fakeyou.com/src/components/common/CheckableTag/CheckableTag.scss @@ -0,0 +1,39 @@ +@import "scss/colours.scss"; + +.checkable-tag { + font-weight: 500; + border-radius: 0.5rem; + font-size: 13px; + padding: 6px 12px; + background-color: $secondary; + color: $white; + cursor: pointer; + transition: 0.2s; + -moz-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + border: 2px solid $secondary; + + &.checked { + background-color: lighten($secondary, 10%) !important; + color: $white; + border: 2px solid $primary !important; + } + + .remove-icon { + align-items: center; + font-size: 14px; + transition: 0.2s; + } + + &:hover { + background-color: lighten($secondary, 5%); + border: 2px solid lighten($secondary, 5%); + } + + &:hover > .remove-icon { + color: $primary; + } +} diff --git a/packages/fakeyou.com/src/components/common/CheckableTag/CheckableTag.tsx b/packages/fakeyou.com/src/components/common/CheckableTag/CheckableTag.tsx new file mode 100644 index 0000000000..6e07d0e74d --- /dev/null +++ b/packages/fakeyou.com/src/components/common/CheckableTag/CheckableTag.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import "./CheckableTag.scss"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faXmark } from "@fortawesome/pro-solid-svg-icons"; + +interface CheckableTagProps { + tag: string; + isSelected: boolean; + onToggle: (tag: string) => void; +} + +export default function CheckableTag({ + tag, + isSelected, + onToggle, +}: CheckableTagProps) { + return ( +
onToggle(tag)} + > + {tag} + {isSelected && ( + + )} +
+ ); +} diff --git a/packages/fakeyou.com/src/components/common/CheckableTag/index.tsx b/packages/fakeyou.com/src/components/common/CheckableTag/index.tsx new file mode 100644 index 0000000000..877c50fb3c --- /dev/null +++ b/packages/fakeyou.com/src/components/common/CheckableTag/index.tsx @@ -0,0 +1 @@ +export { default } from "./CheckableTag"; diff --git a/packages/fakeyou.com/src/components/common/Checkbox/Checkbox.tsx b/packages/fakeyou.com/src/components/common/Checkbox/Checkbox.tsx index 35826acf2a..8f3d0b2176 100644 --- a/packages/fakeyou.com/src/components/common/Checkbox/Checkbox.tsx +++ b/packages/fakeyou.com/src/components/common/Checkbox/Checkbox.tsx @@ -25,6 +25,6 @@ export default function Checkbox({ checked, className = "", label = "", name = " - { label && } + { label && } }; \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/common/Container/Container.tsx b/packages/fakeyou.com/src/components/common/Container/Container.tsx new file mode 100644 index 0000000000..965c52dff4 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/Container/Container.tsx @@ -0,0 +1,35 @@ +import React from "react"; + +interface ContainerProps { + children: React.ReactNode; + type?: "full" | "padded" | "panel"; + className?: string; +} + +export default function PageContainer({ + children, + type = "full", + className = "", +}: ContainerProps) { + let containerClass = ""; + + switch (type) { + case "full": + containerClass = "container-fluid"; + break; + case "padded": + containerClass = "container"; + break; + case "panel": + containerClass = "container-panel"; + break; + default: + containerClass = "container-fluid"; + break; + } + + // Merge the classNames + const mergedClassNames = `${containerClass} ${className}`; + + return
{children}
; +} diff --git a/packages/fakeyou.com/src/components/common/Container/index.tsx b/packages/fakeyou.com/src/components/common/Container/index.tsx new file mode 100644 index 0000000000..c81c92aeb0 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/Container/index.tsx @@ -0,0 +1 @@ +export { default } from "./Container"; diff --git a/packages/fakeyou.com/src/components/common/DataTable/DataTable.scss b/packages/fakeyou.com/src/components/common/DataTable/DataTable.scss new file mode 100644 index 0000000000..e5299b7c8e --- /dev/null +++ b/packages/fakeyou.com/src/components/common/DataTable/DataTable.scss @@ -0,0 +1,34 @@ +@import "scss/colours.scss"; + +table { + font-size: 14px; + margin-bottom: 0 !important; +} + +td { + padding: 8px 16px !important; + min-width: 124px !important; +} + +.no-outer-border { + border: none !important; +} + +.no-outer-border > thead > tr > th:first-child, +.no-outer-border > tbody > tr > td:first-child { + border-left: none !important; +} + +.no-outer-border > thead > tr > th:last-child, +.no-outer-border > tbody > tr > td:last-child { + border-right: none !important; +} + +.no-outer-border > tbody > tr:last-child > td { + border-bottom: none !important; +} + +.data-table-property { + font-weight: 500 !important; + background-color: lighten($panel, 7%) !important; +} diff --git a/packages/fakeyou.com/src/components/common/DataTable/DataTable.tsx b/packages/fakeyou.com/src/components/common/DataTable/DataTable.tsx new file mode 100644 index 0000000000..539e82317e --- /dev/null +++ b/packages/fakeyou.com/src/components/common/DataTable/DataTable.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import "./DataTable.scss"; +import { Link } from "react-router-dom"; + +type Data = { + property: string; + value: string; + link?: string; +}; + +interface DataTableProps { + data: Data[]; +} + +export default function DataTable({ data }: DataTableProps) { + return ( + + + {data.map((row, index) => ( + + + {row.link ? ( + + ) : ( + + )} + + ))} + +
{row.property} + {row.value} + {row.value}
+ ); +} diff --git a/packages/fakeyou.com/src/components/common/DataTable/index.tsx b/packages/fakeyou.com/src/components/common/DataTable/index.tsx new file mode 100644 index 0000000000..e8a1b251a1 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/DataTable/index.tsx @@ -0,0 +1 @@ +export { default } from "./DataTable"; diff --git a/packages/fakeyou.com/src/components/common/Input/Input.tsx b/packages/fakeyou.com/src/components/common/Input/Input.tsx new file mode 100644 index 0000000000..4697f07970 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/Input/Input.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { IconDefinition } from "@fortawesome/fontawesome-svg-core"; + +interface InputProps extends React.InputHTMLAttributes { + icon?: IconDefinition; + label?: string; + textArea?: boolean; +} + +export default function Input({ label, icon, textArea, ...rest }: InputProps) { + return ( +
+ {label && } + +
+ {icon && ( + + )} + +
+
+ ); +} diff --git a/packages/fakeyou.com/src/components/common/Input/index.tsx b/packages/fakeyou.com/src/components/common/Input/index.tsx new file mode 100644 index 0000000000..a50d7d110e --- /dev/null +++ b/packages/fakeyou.com/src/components/common/Input/index.tsx @@ -0,0 +1 @@ +export { default } from "./Input"; diff --git a/packages/fakeyou.com/src/components/common/ModelSearch/ModelSearch.tsx b/packages/fakeyou.com/src/components/common/ModelSearch/ModelSearch.tsx new file mode 100644 index 0000000000..7b84ad5f0c --- /dev/null +++ b/packages/fakeyou.com/src/components/common/ModelSearch/ModelSearch.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import Input from "../Input"; +import { faSearch } from "@fortawesome/pro-solid-svg-icons"; + +interface ModelSearchProps { + value: string; + onChange: (value: string) => void; +} + +export default function ModelSearch({ value, onChange }: ModelSearchProps) { + return ( + onChange(e.target.value)} + placeholder="Search..." + /> + ); +} diff --git a/packages/fakeyou.com/src/components/common/ModelSearch/index.tsx b/packages/fakeyou.com/src/components/common/ModelSearch/index.tsx new file mode 100644 index 0000000000..e557975288 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/ModelSearch/index.tsx @@ -0,0 +1 @@ +export { default } from "./ModelSearch"; diff --git a/packages/fakeyou.com/src/components/common/ModelSearchResults/ModelSearchResults.scss b/packages/fakeyou.com/src/components/common/ModelSearchResults/ModelSearchResults.scss new file mode 100644 index 0000000000..0105a9fc09 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/ModelSearchResults/ModelSearchResults.scss @@ -0,0 +1,40 @@ +@import "scss/colours.scss"; + +.model-search-results { + background-color: lighten($panel, 6%); + border-radius: 0.5rem; + border: 1px solid lighten($panel, 12%); + + .creator-name { + font-weight: 400; + font-size: 14px; + color: $white; + margin-bottom: 0.5rem; + } + + .type-tag { + font-weight: 600; + border-radius: 0.35rem; + font-size: 13px; + padding: 4px 8px; + background-color: $secondary; + color: $white; + transition: 0.2s; + -moz-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + height: 100%; + + &.tt2 { + color: #494ea9; + background-color: #c6c7e1; + } + } + + .model-details { + color: rgba($white, 0.8); + font-size: 14px; + } +} diff --git a/packages/fakeyou.com/src/components/common/ModelSearchResults/ModelSearchResults.tsx b/packages/fakeyou.com/src/components/common/ModelSearchResults/ModelSearchResults.tsx new file mode 100644 index 0000000000..65f808499a --- /dev/null +++ b/packages/fakeyou.com/src/components/common/ModelSearchResults/ModelSearchResults.tsx @@ -0,0 +1,71 @@ +import React from "react"; +import "./ModelSearchResults.scss"; +import { Link } from "react-router-dom"; +import Button from "../Button"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + faChartSimple, + faComment, + faHeart, +} from "@fortawesome/pro-solid-svg-icons"; + +interface ModelSearchResultsProps { + data: any[]; +} + +function shortenNumber(num: number): string { + if (Math.abs(num) < 1e3) return num.toString(); + if (Math.abs(num) < 1e6) return (num / 1e3).toFixed(1) + "k"; + if (Math.abs(num) < 1e9) return (num / 1e6).toFixed(1) + "M"; + return num.toString(); +} + +export default function ModelSearchResults({ data }: ModelSearchResultsProps) { + return ( +
+ {data.map((item) => { + return ( +
+
+
{item.name}
+

+ by{" "} + + {item.creator} + +

+
+
+ + {item.type} + +
+
+ + {item.likes && shortenNumber(item.likes)} +
+
+ + {item.uses && shortenNumber(item.uses)} +
+
+ + {item.comments && shortenNumber(item.comments)} +
+
+
+
+
+
+
+
+ ); + })} +
+ ); +} diff --git a/packages/fakeyou.com/src/components/common/ModelSearchResults/index.tsx b/packages/fakeyou.com/src/components/common/ModelSearchResults/index.tsx new file mode 100644 index 0000000000..e1326737cc --- /dev/null +++ b/packages/fakeyou.com/src/components/common/ModelSearchResults/index.tsx @@ -0,0 +1 @@ +export { default } from "./ModelSearchResults"; diff --git a/packages/fakeyou.com/src/components/common/ModelTags/ModelTags.tsx b/packages/fakeyou.com/src/components/common/ModelTags/ModelTags.tsx new file mode 100644 index 0000000000..474334e460 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/ModelTags/ModelTags.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import CheckableTag from "../CheckableTag"; + +interface ModelTagsProps { + tags: string[]; + selectedTags: string[]; + onSelectTag: (tag: string) => void; +} + +export default function ModelTags({ + tags, + selectedTags, + onSelectTag, +}: ModelTagsProps) { + return ( +
+ {tags.map((tag) => ( + + ))} +
+ ); +} diff --git a/packages/fakeyou.com/src/components/common/ModelTags/index.tsx b/packages/fakeyou.com/src/components/common/ModelTags/index.tsx new file mode 100644 index 0000000000..1719f41058 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/ModelTags/index.tsx @@ -0,0 +1 @@ +export { default } from "./ModelTags"; diff --git a/packages/fakeyou.com/src/components/common/PageContainer/PageContainer.tsx b/packages/fakeyou.com/src/components/common/PageContainer/PageContainer.tsx deleted file mode 100644 index c2088b8e9a..0000000000 --- a/packages/fakeyou.com/src/components/common/PageContainer/PageContainer.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { container } from "data/animation"; -import { motion } from "framer-motion"; -import React from "react"; - -interface PageContainerProps { - children: React.ReactNode; - padding?: boolean; -} - -export default function PageContainer({ - children, - padding, -}: PageContainerProps) { - return ( - - {children} - - ); -} diff --git a/packages/fakeyou.com/src/components/common/Panel/Panel.tsx b/packages/fakeyou.com/src/components/common/Panel/Panel.tsx index 6fda465884..f86e65d2f9 100644 --- a/packages/fakeyou.com/src/components/common/Panel/Panel.tsx +++ b/packages/fakeyou.com/src/components/common/Panel/Panel.tsx @@ -1,6 +1,4 @@ import React from "react"; -import { motion } from "framer-motion"; -import { panel } from "data/animation"; interface PanelProps { children: React.ReactNode; @@ -11,13 +9,11 @@ interface PanelProps { export default function Panel({ children, padding, mb, mt }: PanelProps) { return ( - -
- {children} -
-
+ > + {children} + ); } diff --git a/packages/fakeyou.com/src/components/common/Panel/index.tsx b/packages/fakeyou.com/src/components/common/Panel/index.tsx new file mode 100644 index 0000000000..76888ed957 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/Panel/index.tsx @@ -0,0 +1 @@ +export { default } from "./Panel"; diff --git a/packages/fakeyou.com/src/components/common/SegmentButtons/SegmentButtons.tsx b/packages/fakeyou.com/src/components/common/SegmentButtons/SegmentButtons.tsx index d13c2384a6..9f9e66d4ff 100644 --- a/packages/fakeyou.com/src/components/common/SegmentButtons/SegmentButtons.tsx +++ b/packages/fakeyou.com/src/components/common/SegmentButtons/SegmentButtons.tsx @@ -11,8 +11,9 @@ interface Props { export default function SegmentButtons({ name, onChange, options = [], value: inValue = "" }: Props) { // const onClick = ({ target }: any) => onChange(); return
    - { options.map(({ label = "", value = "" },i: number) =>
  • onChange({ target: { name, type: "option", value } }) }}>{ label }
  • ) }
; diff --git a/packages/fakeyou.com/src/components/common/Select/Select.scss b/packages/fakeyou.com/src/components/common/Select/Select.scss new file mode 100644 index 0000000000..325d4a91cf --- /dev/null +++ b/packages/fakeyou.com/src/components/common/Select/Select.scss @@ -0,0 +1,72 @@ +@import "scss/colours.scss"; + +.select__control { + &.select { + height: 48px; + border-width: 2px; + padding-left: 0.15rem; + padding-right: 0.15rem; + background-color: $input-dark; + border-radius: 0.5rem; + border-color: $input-dark !important; + box-shadow: none; + cursor: pointer; + + &.no-padding { + padding-left: 0.5rem; + } + + &:hover { + background-color: lighten($input-dark, 5%); + border-color: lighten($input-dark, 5%) !important; + } + + &.focused { + border-color: $primary !important; + } + + &.with-icon { + padding-left: 2rem; + } + + &.rounded-full { + border-radius: 50px; + } + } + + .select-option { + background-color: $input-dark; + cursor: pointer; + padding: 0.5rem 1rem; + + &:hover { + background-color: lighten($input-dark, 8%); + } + } + + .select-input, + .select-value { + color: $white; + } + + .select-container { + background: $input-dark; + margin-top: 6px; + padding: 0; + border-radius: 0.5rem; + z-index: 3; + } + + .select-separator { + width: 1px; + opacity: 0.3; + } + + .select-placeholder { + color: rgba($white, 0.5) !important; + } +} + +.select-small { + font-size: 14px !important; +} diff --git a/packages/fakeyou.com/src/components/common/Select/Select.tsx b/packages/fakeyou.com/src/components/common/Select/Select.tsx new file mode 100644 index 0000000000..7f1e44e8a7 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/Select/Select.tsx @@ -0,0 +1,64 @@ +import React from "react"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { IconDefinition } from "@fortawesome/fontawesome-svg-core"; +import "./Select.scss"; +import { + default as ReactSelect, + Props as ReactSelectProps, +} from "react-select"; + +interface SelectProps extends ReactSelectProps { + icon?: IconDefinition; + label?: string; + rounded?: boolean; + small?: boolean; +} + +export default function Select({ + label, + icon, + rounded, + small, + ...rest +}: SelectProps) { + const SelectFieldClass = { + control: (state: any) => + state.isFocused + ? `select focused ${icon && "with-icon"} ${rounded && "rounded-full"}` + : `select ${icon && "with-icon"} ${rounded && "rounded-full"}`, + option: (state: any) => + state.isFocused ? "select-option" : "select-option", + input: (state: any) => (state.isFocused ? "select-input" : "select-input"), + placeholder: (state: any) => + state.isFocused ? "select-placeholder" : "select-placeholder", + singleValue: (state: any) => + state.isFocused ? "select-value" : "select-value", + menu: (state: any) => + state.isFocused ? "select-container" : "select-container", + indicatorSeparator: (state: any) => + state.isFocused ? "select-separator" : "select-separator", + }; + + return ( +
+ {label && } + +
+ {icon && ( + + )} +
+ +
+
+
+ ); +} diff --git a/packages/fakeyou.com/src/components/common/Select/index.tsx b/packages/fakeyou.com/src/components/common/Select/index.tsx new file mode 100644 index 0000000000..cd39325ed3 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/Select/index.tsx @@ -0,0 +1 @@ +export { default } from "./Select"; diff --git a/packages/fakeyou.com/src/components/common/ShareButton/ShareButton.tsx b/packages/fakeyou.com/src/components/common/ShareButton/ShareButton.tsx index a5501655b7..ed1fe885d2 100644 --- a/packages/fakeyou.com/src/components/common/ShareButton/ShareButton.tsx +++ b/packages/fakeyou.com/src/components/common/ShareButton/ShareButton.tsx @@ -35,8 +35,8 @@ export default function ShareButton(props: ShareButtonProps) {