diff --git a/packages/fakeyou.com/package.json b/packages/fakeyou.com/package.json index f89e903dca..a0849337e4 100644 --- a/packages/fakeyou.com/package.json +++ b/packages/fakeyou.com/package.json @@ -33,7 +33,7 @@ "axios": "^0.21.0", "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/src/components/common/Accordion/Accordion.scss b/packages/fakeyou.com/src/components/common/Accordion/Accordion.scss index 8fd9d8ee37..fcb40c3a17 100644 --- a/packages/fakeyou.com/src/components/common/Accordion/Accordion.scss +++ b/packages/fakeyou.com/src/components/common/Accordion/Accordion.scss @@ -2,7 +2,7 @@ .fy-accordion-item { border: 1px solid rgba($white, 0.12); - border-radius: 5px; + border-radius: 0.5rem; overflow: hidden; } diff --git a/packages/fakeyou.com/src/components/common/Accordion/Accordion.tsx b/packages/fakeyou.com/src/components/common/Accordion/Accordion.tsx index 7d6daab7c6..4eceaeee37 100644 --- a/packages/fakeyou.com/src/components/common/Accordion/Accordion.tsx +++ b/packages/fakeyou.com/src/components/common/Accordion/Accordion.tsx @@ -1,4 +1,10 @@ -import React, { useState, useRef, useEffect } from "react"; +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"; @@ -14,50 +20,62 @@ interface AccordionItemProps { children: React.ReactNode; } +interface AccordionContextType { + openItems: string[]; + toggleItem: (title: string) => void; +} + +const AccordionContext = createContext( + undefined +); + function AccordionItem({ title, defaultOpen = false, children, }: AccordionItemProps) { - const [isOpen, setIsOpen] = useState(defaultOpen); + const { openItems, toggleItem } = useContext( + AccordionContext + ) as AccordionContextType; + const isOpen = openItems.includes(title); + const [contentHeight, setContentHeight] = useState( - defaultOpen ? undefined : 0 + isOpen ? undefined : 0 ); const contentRef = useRef(null); useEffect(() => { - if (isOpen && contentRef.current) { - setContentHeight(contentRef.current.scrollHeight); - } else { - setContentHeight(0); + if (contentRef.current) { + setContentHeight(isOpen ? contentRef.current.scrollHeight : 0); } }, [isOpen]); const heightProps = useSpring({ - height: `${contentHeight}px`, - from: { - height: defaultOpen ? "auto" : "0px", - config: { tension: 300, friction: 25 }, - }, + height: isOpen ? `${contentHeight}px` : "0px", + config: { tension: 300, friction: 25, clamp: true }, }); const contentOpacityProps = useSpring({ opacity: isOpen ? 1 : 0, - config: { tension: 300, friction: 25 }, + config: { tension: 300, friction: 25, clamp: true }, }); + const handleClick = () => { + toggleItem(title); + }; + return (
setIsOpen(!isOpen)} + onClick={handleClick} > {title}
- + {children}
; + 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; diff --git a/packages/fakeyou.com/src/components/common/DataTable/DataTable.scss b/packages/fakeyou.com/src/components/common/DataTable/DataTable.scss index 1d3483e027..dc19b4a6c7 100644 --- a/packages/fakeyou.com/src/components/common/DataTable/DataTable.scss +++ b/packages/fakeyou.com/src/components/common/DataTable/DataTable.scss @@ -1,11 +1,13 @@ @import "scss/colours.scss"; table { + font-size: 14px; margin-bottom: 0 !important; } td { padding: 8px 16px !important; + min-width: 124px !important; } .no-outer-border { diff --git a/packages/fakeyou.com/src/hooks/useTimeAgo/index.ts b/packages/fakeyou.com/src/hooks/useTimeAgo/index.ts new file mode 100644 index 0000000000..039f2a67f4 --- /dev/null +++ b/packages/fakeyou.com/src/hooks/useTimeAgo/index.ts @@ -0,0 +1 @@ +export { default } from "./useTimeAgo"; diff --git a/packages/fakeyou.com/src/hooks/useTimeAgo/useTimeAgo.tsx b/packages/fakeyou.com/src/hooks/useTimeAgo/useTimeAgo.tsx new file mode 100644 index 0000000000..4137c46b27 --- /dev/null +++ b/packages/fakeyou.com/src/hooks/useTimeAgo/useTimeAgo.tsx @@ -0,0 +1,44 @@ +import { useEffect, useState } from "react"; + +function useTimeAgo(timestamp: string) { + const [timeAgo, setTimeAgo] = useState(""); + + useEffect(() => { + const calculateTimeAgo = () => { + const now = new Date(); + const then = new Date(timestamp); + + const seconds = Math.floor((now.getTime() - then.getTime()) / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + const days = Math.floor(hours / 24); + const months = Math.floor(days / 30); + const years = Math.floor(months / 12); + + if (years > 0) { + setTimeAgo(`${years} ${years === 1 ? "year" : "years"} ago`); + } else if (months > 0) { + setTimeAgo(`${months} ${months === 1 ? "month" : "months"} ago`); + } else if (days > 0) { + setTimeAgo(`${days} ${days === 1 ? "day" : "days"} ago`); + } else if (hours > 0) { + setTimeAgo(`${hours} ${hours === 1 ? "hour" : "hours"} ago`); + } else if (minutes > 0) { + setTimeAgo(`${minutes} ${minutes === 1 ? "minute" : "minutes"} ago`); + } else { + setTimeAgo(`${seconds} ${seconds === 1 ? "second" : "seconds"} ago`); + } + }; + + calculateTimeAgo(); + + // Update the time difference every minute + const intervalId = setInterval(calculateTimeAgo, 60000); + + return () => clearInterval(intervalId); + }, [timestamp]); + + return timeAgo; +} + +export default useTimeAgo; diff --git a/packages/fakeyou.com/src/scss/site-refresh.scss b/packages/fakeyou.com/src/scss/site-refresh.scss index 9943d8b6f9..49acbb1051 100644 --- a/packages/fakeyou.com/src/scss/site-refresh.scss +++ b/packages/fakeyou.com/src/scss/site-refresh.scss @@ -148,7 +148,7 @@ styles with important tags: } .container { - max-width: 1080px !important; + max-width: 1200px !important; } .container, diff --git a/packages/fakeyou.com/src/v2/view/pages/media/MediaDataTypes.ts b/packages/fakeyou.com/src/v2/view/pages/media/MediaDataTypes.ts index cd22b4597f..97148a2014 100644 --- a/packages/fakeyou.com/src/v2/view/pages/media/MediaDataTypes.ts +++ b/packages/fakeyou.com/src/v2/view/pages/media/MediaDataTypes.ts @@ -12,6 +12,8 @@ export default interface MediaData { color_index: number; }; }; + model_used: string; + model_link: string; creator_set_visibility: string; created_at: string; updated_at: string; diff --git a/packages/fakeyou.com/src/v2/view/pages/media/MediaPage.tsx b/packages/fakeyou.com/src/v2/view/pages/media/MediaPage.tsx index 955a91d5be..4a6e111a3b 100644 --- a/packages/fakeyou.com/src/v2/view/pages/media/MediaPage.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/media/MediaPage.tsx @@ -14,6 +14,7 @@ import { faCircleExclamation } from "@fortawesome/pro-solid-svg-icons"; import Accordion from "components/common/Accordion"; import DataTable from "components/common/DataTable"; import { Gravatar } from "@storyteller/components/src/elements/Gravatar"; +import useTimeAgo from "hooks/useTimeAgo"; interface MediaPageProps { sessionWrapper: SessionWrapper; @@ -35,9 +36,11 @@ let dummyMediaData = { color_index: 3, }, }, + model_used: "Ash Ketchum", + model_link: "/tts/TM:6g1mfb9b6fb8", creator_set_visibility: "public", - created_at: "2023-10-12T07:49:53Z", - updated_at: "2023-10-12T07:49:53Z", + created_at: "2023-05-12T07:49:53Z", + updated_at: "2023-05-12T07:49:53Z", }; export default function MediaPage({ sessionWrapper }: MediaPageProps) { @@ -46,6 +49,8 @@ export default function MediaPage({ sessionWrapper }: MediaPageProps) { const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(false); + const timeCreated = useTimeAgo(mediaData?.created_at || ""); + // Simulate API call useEffect(() => { setTimeout(() => { @@ -88,12 +93,12 @@ export default function MediaPage({ sessionWrapper }: MediaPageProps) { />
-
+

-
+

@@ -123,7 +128,10 @@ export default function MediaPage({ sessionWrapper }: MediaPageProps) { { property: "Type", value: mediaData.media_type }, { property: "Created at", value: mediaData.created_at }, { property: "Visibility", value: mediaData.creator_set_visibility }, - { property: "Model Used", value: "Model Name" }, + { + property: "Model", + value: mediaData.model_used, + }, ]; return ( @@ -134,13 +142,13 @@ export default function MediaPage({ sessionWrapper }: MediaPageProps) { />
-
+
{renderMediaComponent(mediaData)}
-
-
+
+
{mediaData.maybe_creator_user.display_name} + {timeCreated}
diff --git a/yarn.lock b/yarn.lock index b6f99c3d43..16f51c2909 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4570,6 +4570,7 @@ __metadata: dependencies: "@react-spring/types": ^9.7.3 craco: ^0.0.3 + date-fns: 2.22.1 lerna: ^4.0.0 yarn: ^1.22.19 languageName: unknown @@ -8966,6 +8967,13 @@ __metadata: languageName: node linkType: hard +"date-fns@npm:2.22.1": + version: 2.22.1 + resolution: "date-fns@npm:2.22.1" + checksum: 7ff97cd605af50c02f341687c2cafd218839a1aace67965374989855a13f76dc4fe52e0e38c343c1ad1f8399787cb6839a0b14a669c44b30550c287300b1bb50 + languageName: node + linkType: hard + "date-fns@npm:^2.22.1": version: 2.28.0 resolution: "date-fns@npm:2.28.0"