turn card into links, renamed favorite button to bookmark

This commit is contained in:
BFlat committed 2023-12-26 21:57:34 +07:00
1 parent d44eca81d9
commit 6cdff7f9d4
13 files changed
+354 -347

No files matched your search

@@ -73,7 +73,9 @@ export default function AudioPlayer({ src, id }: AudioPlayerProps) {
}
}, [isPlayerOnScreen, isPlaying]);
const togglePlayPause = () => {
const togglePlayPause = (event: any) => {
event.preventDefault();
event.stopPropagation();
if (waveSurferRef.current) {
if (!isPlaying) {
setCurrentPlayingId(id);
@@ -14,6 +14,7 @@
user-select: none;
margin-bottom: 10px;
padding: 4px 8px;
color: $white;
&.shadow {
box-shadow: 0 0 12px rgba($bg, 0.4);
@@ -5,16 +5,17 @@
align-items: center;
justify-content: center;
background-color: rgba(lighten($secondary, 5%), 0.9);
color: inherit;
color: rgba($white, 0.7);
border: none;
padding: 0;
font: inherit;
font-size: 14px;
cursor: pointer;
outline: inherit;
font-size: 16px;
transition: 0.2s;
padding: 3px 8px;
min-width: 56px;
min-width: 52px;
border-radius: 0.5rem;
&.shadow {
@@ -4,10 +4,10 @@ import { faBookmark } from "@fortawesome/pro-solid-svg-icons";
import { faBookmark as faBookmarkOutline } from "@fortawesome/pro-regular-svg-icons";
import Tippy from "@tippyjs/react";
import "tippy.js/dist/tippy.css";
import "./FavoriteButton.scss";
import "./BookmarkButton.scss";
// import useShortenNumber from "hooks/useShortenNumber";
interface FavoriteButtonProps {
interface BookmarkButtonProps {
entityToken?: string;
entityType?: string;
initialToggled?: boolean;
@@ -17,7 +17,7 @@ interface FavoriteButtonProps {
large?: boolean;
}
export default function FavoriteButton({
export default function BookmarkButton({
entityToken = "",
entityType = "",
initialToggled = false,
@@ -25,14 +25,13 @@ export default function FavoriteButton({
favoriteCount,
overlay,
large,
}: FavoriteButtonProps) {
}: BookmarkButtonProps) {
const [isToggled, setIsToggled] = useState(initialToggled);
const [isLoading, setIsLoading] = useState(false);
const handleClick = async () => {
setIsLoading(true);
onToggle(entityToken, entityType)
.then((isToggled: boolean) => {
onToggle(entityToken, entityType).then((isToggled: boolean) => {
setIsToggled(isToggled);
setIsLoading(false);
});
@@ -64,7 +63,7 @@ export default function FavoriteButton({
placement="bottom"
>
<button
// onClick={handleClick} // unnecessary, parent has onClick, runs twice
// onClick={handleClick} // unnecessary, parent has onClick, runs twice
disabled={isLoading}
className={`${buttonClass} ${buttonShadow} ${large ? "large" : ""}`}
>
@@ -0,0 +1 @@
export { default } from "./BookmarkButton";
@@ -1,11 +1,11 @@
import React from "react";
import { useHistory } from "react-router-dom";
import { Link } from "react-router-dom";
import Card from "../Card";
import AudioPlayer from "components/common/AudioPlayer";
import useTimeAgo from "hooks/useTimeAgo";
import Badge from "components/common/Badge";
import LikeButton from "components/common/LikeButton";
import FavoriteButton from "components/common/FavoriteButton";
import BookmarkButton from "components/common/BookmarkButton";
import CreatorName from "../CreatorName";
import { faArrowRight } from "@fortawesome/pro-solid-svg-icons";
import Button from "components/common/Button";
@@ -24,26 +24,13 @@ export default function AudioCard({
showCreator,
showCover,
}: AudioCardProps) {
const history = useHistory();
const handleCardClick = () => {
if (type === "media") {
history.push(`/media/${data.token}`);
} else if (type === "weights") {
history.push(`/weight/${data.weight_token}`);
}
};
const linkUrl =
type === "media" ? `/media/${data.token}` : `/weight/${data.weight_token}`;
const handleInnerClick = (event: any) => {
event.stopPropagation();
};
// const handleUseModel = () => {
// if (type === "weights") {
// history.push(`/weights/${data.token}`);
// }
// };
const timeAgo = useTimeAgo(data.created_at);
const handleLike = async () => {
@@ -55,113 +42,120 @@ export default function AudioCard({
useWeightTypeInfo(data.weights_type);
return (
<Card padding={true} onClick={handleCardClick}>
{type === "media" && (
<>
<div className="mb-3">
<div className="d-flex align-items-center">
<div className="d-flex flex-grow-1 align-items-center gap-2">
<Badge label="Audio" color="teal" />
</div>
</div>
<h6 className="fw-semibold text-white mb-1 mt-3">
{data.weight_name}
</h6>
<p className="fs-7 opacity-75">{timeAgo}</p>
</div>
<div onClick={handleInnerClick}>
<AudioPlayer src={data.public_bucket_path} id={data.token} />
</div>
<hr className="my-3" />
<div
className="d-flex align-items-center gap-2"
onClick={handleInnerClick}
>
{showCreator && (
<div className="flex-grow-1">
<CreatorName
displayName={data.maybe_creator?.display_name || "Anonymous"}
gravatarHash={data.maybe_creator?.gravatar_hash || null}
avatarIndex={
data.maybe_creator?.default_avatar.image_index || 0
}
backgroundIndex={
data.maybe_creator?.default_avatar.color_index || 0
}
username={data.maybe_creator?.username || "anonymous"}
/>
</div>
)}
<div>
<LikeButton onToggle={handleLike} likeCount={data.like || 0} />
</div>
</div>
</>
)}
{type === "weights" && (
<>
<div className="d-flex">
{showCover && (
<WeightCoverImage src="/images/avatars/default-pfp.png" />
)}
<div className="flex-grow-1">
<Link to={linkUrl}>
<Card padding={true} canHover={true}>
{type === "media" && (
<>
<div className="mb-3">
<div className="d-flex align-items-center">
<div className="d-flex flex-grow-1">
<Badge label={weightBadgeLabel} color={weightBadgeColor} />
<div className="d-flex flex-grow-1 align-items-center gap-2">
<Badge label="Audio" color="teal" />
</div>
<Button
icon={faArrowRight}
iconFlip={true}
variant="link"
label="Use"
onClick={handleCardClick}
className="fs-7"
/>
</div>
<div className="d-flex align-items-center mt-3">
<h6 className="fw-semibold text-white mb-1 mt-3">
{data.weight_name}
</h6>
<p className="fs-7 opacity-75">{timeAgo}</p>
</div>
<AudioPlayer src={data.public_bucket_path} id={data.token} />
<hr className="my-3" />
<div
className="d-flex align-items-center gap-2"
onClick={handleInnerClick}
>
{showCreator && (
<div className="flex-grow-1">
<h6 className="fw-semibold text-white mb-1">{data.title}</h6>
<p className="fs-7 opacity-75">{timeAgo}</p>
<CreatorName
displayName={
data.maybe_creator?.display_name || "Anonymous"
}
gravatarHash={data.maybe_creator?.gravatar_hash || null}
avatarIndex={
data.maybe_creator?.default_avatar.image_index || 0
}
backgroundIndex={
data.maybe_creator?.default_avatar.color_index || 0
}
username={data.maybe_creator?.username || "anonymous"}
/>
</div>
)}
<div>
<LikeButton onToggle={handleLike} likeCount={data.like || 0} />
</div>
</div>
</>
)}
{type === "weights" && (
<>
<div className="d-flex">
{showCover && (
<WeightCoverImage src="/images/avatars/default-pfp.png" />
)}
<div className="flex-grow-1">
<div className="d-flex align-items-center">
<div className="d-flex flex-grow-1">
<Badge label={weightBadgeLabel} color={weightBadgeColor} />
</div>
<Button
icon={faArrowRight}
iconFlip={true}
variant="link"
label="Use"
className="fs-7"
to={linkUrl}
/>
</div>
<div className="d-flex align-items-center mt-3">
<div className="flex-grow-1">
<h6 className="fw-semibold text-white mb-1">
{data.title}
</h6>
<p className="fs-7 opacity-75">{timeAgo}</p>
</div>
</div>
</div>
</div>
</div>
<hr className="my-3" />
<hr className="my-3" />
<div
className="d-flex align-items-center gap-2"
onClick={handleInnerClick}
>
{showCreator && (
<div className="flex-grow-1">
<CreatorName
displayName={data.creator?.display_name || "Anonymous"}
gravatarHash={data.creator?.gravatar_hash || null}
avatarIndex={data.creator?.default_avatar.image_index || 0}
backgroundIndex={
data.creator?.default_avatar.color_index || 0
}
username={data.creator?.username || "anonymous"}
/>
<div
className="d-flex align-items-center gap-2"
onClick={handleInnerClick}
>
{showCreator && (
<div className="flex-grow-1">
<CreatorName
displayName={data.creator?.display_name || "Anonymous"}
gravatarHash={data.creator?.gravatar_hash || null}
avatarIndex={data.creator?.default_avatar.image_index || 0}
backgroundIndex={
data.creator?.default_avatar.color_index || 0
}
username={data.creator?.username || "anonymous"}
/>
</div>
)}
<div>
<LikeButton onToggle={handleLike} likeCount={data.likes} />
</div>
)}
<div>
<LikeButton onToggle={handleLike} likeCount={data.likes} />
<BookmarkButton
onToggle={handleLike}
favoriteCount={data.likes}
/>
</div>
<FavoriteButton onToggle={handleLike} favoriteCount={data.likes} />
</div>
</>
)}
</Card>
</>
)}
</Card>
</Link>
);
}
@@ -5,13 +5,19 @@ interface CardProps {
padding?: boolean;
children?: React.ReactNode;
onClick?: () => void;
canHover?: boolean;
}
export default function Card({ padding, children, onClick }: CardProps) {
export default function Card({
padding,
children,
onClick,
canHover,
}: CardProps) {
return (
<div
className={`card ${padding ? "p-3" : ""} ${
onClick ? "card-clickable" : ""
onClick || canHover ? "card-clickable" : ""
}`}
onClick={onClick}
>
@@ -1,10 +1,10 @@
import React from "react";
import { useHistory } from "react-router-dom";
import { Link } from "react-router-dom";
import Card from "../Card";
import useTimeAgo from "hooks/useTimeAgo";
import Badge from "components/common/Badge";
import LikeButton from "components/common/LikeButton";
import FavoriteButton from "components/common/FavoriteButton";
import BookmarkButton from "components/common/BookmarkButton";
import CreatorName from "../CreatorName";
import Button from "components/common/Button";
import { faArrowRight } from "@fortawesome/pro-solid-svg-icons";
@@ -18,15 +18,8 @@ interface ImageCardProps {
}
export default function ImageCard({ data, type, showCreator }: ImageCardProps) {
const history = useHistory();
const handleCardClick = () => {
if (type === "media") {
history.push(`/media/${data.token}`);
} else if (type === "weights") {
history.push(`/weight/${data.weight_token}`);
}
};
const linkUrl =
type === "media" ? `/media/${data.token}` : `/weight/${data.weight_token}`;
const handleInnerClick = (event: any) => {
event.stopPropagation();
@@ -45,130 +38,130 @@ export default function ImageCard({ data, type, showCreator }: ImageCardProps) {
const imageLink = new BucketConfig().getGcsUrl(data.public_bucket_path);
return (
<Card padding={false} onClick={handleCardClick}>
{type === "media" && (
<>
<img
src={imageLink}
alt={data.weight_name}
className="card-img"
/>
<div className="card-img-overlay">
<div className="card-img-gradient" />
<Link to={linkUrl}>
<Card padding={false} canHover={true}>
{type === "media" && (
<>
<img src={imageLink} alt={data.weight_name} className="card-img" />
<div className="card-img-overlay">
<div className="card-img-gradient" />
<div className="d-flex align-items-center">
<div className="d-flex flex-grow-1">
<Badge label="Image" color="ultramarine" overlay={true} />
</div>
</div>
<div className="card-img-overlay-text">
<div>
<p className="fs-7 opacity-75 mb-0">{timeAgo}</p>
</div>
<hr className="my-2" />
<div
className="d-flex align-items-center gap-2"
onClick={handleInnerClick}
>
{showCreator && (
<div className="flex-grow-1">
<CreatorName
displayName={
data.maybe_creator?.display_name || "Anonymous"
}
gravatarHash={data.maybe_creator?.gravatar_hash || null}
avatarIndex={
data.maybe_creator?.default_avatar.image_index || 0
}
backgroundIndex={
data.maybe_creator?.default_avatar.color_index || 0
}
username={data.maybe_creator?.username || "anonymous"}
/>
</div>
)}
<div>
<LikeButton onToggle={handleLike} likeCount={data.likes} />
<div className="d-flex align-items-center">
<div className="d-flex flex-grow-1">
<Badge label="Image" color="ultramarine" overlay={true} />
</div>
</div>
</div>
</div>
</>
)}
{type === "weights" && (
<>
<img
src={
data.maybe_cover_image_public_bucket_path ||
"/images/avatars/default-pfp.png"
}
alt={data.title}
className="card-img"
/>
<div className="card-img-overlay">
<div className="card-img-gradient" />
<div className="d-flex align-items-center">
<div className="d-flex flex-grow-1">
<Badge
label={weightBadgeLabel}
color={weightBadgeColor}
overlay={true}
/>
</div>
<Button
icon={faArrowRight}
iconFlip={true}
variant="link"
label="Use"
onClick={handleCardClick}
className="fs-7"
/>
</div>
<div className="card-img-overlay-text">
<div className="d-flex align-items-center mt-3">
<div className="flex-grow-1">
<h6 className="fw-semibold text-white mb-1">{data.title}</h6>
<div className="card-img-overlay-text">
<div>
<p className="fs-7 opacity-75 mb-0">{timeAgo}</p>
</div>
</div>
<hr className="my-2" />
<hr className="my-2" />
<div
className="d-flex align-items-center gap-2"
onClick={handleInnerClick}
>
{showCreator && (
<div className="flex-grow-1">
<CreatorName
displayName={data.creator?.display_name || "Anonymous"}
gravatarHash={data.creator?.gravatar_hash || null}
avatarIndex={
data.creator?.default_avatar.image_index || 0
}
backgroundIndex={
data.creator?.default_avatar.color_index || 0
}
username={data.creator?.username || "anonymous"}
/>
<div
className="d-flex align-items-center gap-2"
onClick={handleInnerClick}
>
{showCreator && (
<div className="flex-grow-1">
<CreatorName
displayName={
data.maybe_creator?.display_name || "Anonymous"
}
gravatarHash={data.maybe_creator?.gravatar_hash || null}
avatarIndex={
data.maybe_creator?.default_avatar.image_index || 0
}
backgroundIndex={
data.maybe_creator?.default_avatar.color_index || 0
}
username={data.maybe_creator?.username || "anonymous"}
/>
</div>
)}
<div>
<LikeButton onToggle={handleLike} likeCount={data.likes} />
</div>
)}
<div>
<LikeButton onToggle={handleLike} likeCount={data.likes} />
</div>
<FavoriteButton onToggle={handleLike} />
</div>
</div>
</div>
</>
)}
</Card>
</>
)}
{type === "weights" && (
<>
<img
src={
data.maybe_cover_image_public_bucket_path ||
"/images/avatars/default-pfp.png"
}
alt={data.title}
className="card-img"
/>
<div className="card-img-overlay">
<div className="card-img-gradient" />
<div className="d-flex align-items-center">
<div className="d-flex flex-grow-1">
<Badge
label={weightBadgeLabel}
color={weightBadgeColor}
overlay={true}
/>
</div>
<Button
icon={faArrowRight}
iconFlip={true}
variant="link"
label="Use"
to={linkUrl}
className="fs-7"
/>
</div>
<div className="card-img-overlay-text">
<div className="d-flex align-items-center mt-3">
<div className="flex-grow-1">
<h6 className="fw-semibold text-white mb-1">
{data.title}
</h6>
<p className="fs-7 opacity-75 mb-0">{timeAgo}</p>
</div>
</div>
<hr className="my-2" />
<div
className="d-flex align-items-center gap-2"
onClick={handleInnerClick}
>
{showCreator && (
<div className="flex-grow-1">
<CreatorName
displayName={data.creator?.display_name || "Anonymous"}
gravatarHash={data.creator?.gravatar_hash || null}
avatarIndex={
data.creator?.default_avatar.image_index || 0
}
backgroundIndex={
data.creator?.default_avatar.color_index || 0
}
username={data.creator?.username || "anonymous"}
/>
</div>
)}
<div>
<LikeButton onToggle={handleLike} likeCount={data.likes} />
</div>
<BookmarkButton onToggle={handleLike} />
</div>
</div>
</div>
</>
)}
</Card>
</Link>
);
}
@@ -1,5 +1,4 @@
import React from "react";
import { useHistory } from "react-router-dom";
import Card from "../Card";
import useTimeAgo from "hooks/useTimeAgo";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -9,6 +8,7 @@ import LikeButton from "components/common/LikeButton";
import Button from "components/common/Button";
import CreatorName from "../CreatorName";
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
import { Link } from "react-router-dom";
interface VideoCardProps {
data: any;
@@ -17,11 +17,8 @@ interface VideoCardProps {
}
export default function VideoCard({ data, type, showCreator }: VideoCardProps) {
const history = useHistory();
const handleCardClick = () => {
history.push(`/media/${data.token}`);
};
const linkUrl =
type === "media" ? `/media/${data.token}` : `/weight/${data.weight_token}`;
const handleInnerClick = (event: any) => {
event.stopPropagation();
@@ -37,104 +34,115 @@ export default function VideoCard({ data, type, showCreator }: VideoCardProps) {
const videoLink = new BucketConfig().getGcsUrl(data.public_bucket_path);
return (
<Card padding={false} onClick={handleCardClick}>
{type === "media" && (
<>
<img
src={videoLink}
alt={data.weight_name}
className="card-video"
/>
<div className="card-img-overlay">
<div className="card-img-gradient" />
<Link to={linkUrl}>
<Card padding={false} canHover={true}>
{type === "media" && (
<>
<img
src={videoLink}
alt={data.weight_name}
className="card-video"
/>
<div className="card-img-overlay">
<div className="card-img-gradient" />
<div className="d-flex align-items-center">
<div className="d-flex flex-grow-1">
<Badge label="Video" color="purple" overlay={true} />
<div className="d-flex align-items-center">
<div className="d-flex flex-grow-1">
<Badge label="Video" color="purple" overlay={true} />
</div>
</div>
</div>
<FontAwesomeIcon icon={faPlayCircle} className="card-video-play" />
<div className="card-img-overlay-text">
<div>
<h6 className="fw-semibold text-white mb-1">
{data.weight_name}
</h6>
<p className="fs-7 opacity-75">{timeAgo}</p>
<hr className="my-2" />
<div
className="d-flex align-items-center gap-2"
onClick={handleInnerClick}
>
{showCreator && (
<div className="flex-grow-1">
<CreatorName
displayName={
data.maybe_creator?.display_name || "Anonymous"
}
gravatarHash={data.maybe_creator?.gravatar_hash || ""}
avatarIndex={
data.maybe_creator?.default_avatar.image_index || ""
}
backgroundIndex={
data.maybe_creator?.default_avatar.color_index || ""
}
username={data.maybe_creator?.username || "anonymous"}
<FontAwesomeIcon
icon={faPlayCircle}
className="card-video-play"
/>
<div className="card-img-overlay-text">
<div>
<h6 className="fw-semibold text-white mb-1">
{data.weight_name}
</h6>
<p className="fs-7 opacity-75">{timeAgo}</p>
<hr className="my-2" />
<div
className="d-flex align-items-center gap-2"
onClick={handleInnerClick}
>
{showCreator && (
<div className="flex-grow-1">
<CreatorName
displayName={
data.maybe_creator?.display_name || "Anonymous"
}
gravatarHash={data.maybe_creator?.gravatar_hash || ""}
avatarIndex={
data.maybe_creator?.default_avatar.image_index || ""
}
backgroundIndex={
data.maybe_creator?.default_avatar.color_index || ""
}
username={data.maybe_creator?.username || "anonymous"}
/>
</div>
)}
<div>
<LikeButton
onToggle={handleLike}
likeCount={data.likes}
/>
</div>
)}
<div>
<LikeButton onToggle={handleLike} likeCount={data.likes} />
</div>
</div>
</div>
</div>
</div>
</>
)}
</>
)}
{type === "weights" && (
<>
<img
src={data.public_bucket_path}
alt={data.weight_name}
className="card-video"
/>
<div className="card-img-overlay">
<div className="card-img-gradient" />
{type === "weights" && (
<>
<img
src={data.public_bucket_path}
alt={data.weight_name}
className="card-video"
/>
<div className="card-img-overlay">
<div className="card-img-gradient" />
<div className="d-flex align-items-center">
<div className="d-flex flex-grow-1">
<Badge label="Video" color="purple" overlay={true} />
<div className="d-flex align-items-center">
<div className="d-flex flex-grow-1">
<Badge label="Video" color="purple" overlay={true} />
</div>
<Button
icon={faArrowRight}
iconFlip={true}
variant="link"
label="Use"
to={linkUrl}
className="fs-7"
/>
</div>
<Button
icon={faArrowRight}
iconFlip={true}
variant="link"
label="Use"
onClick={handleCardClick}
className="fs-7"
<FontAwesomeIcon
icon={faPlayCircle}
className="card-video-play"
/>
</div>
<FontAwesomeIcon icon={faPlayCircle} className="card-video-play" />
<div className="card-img-overlay-text">
<div>
<h6 className="fw-semibold text-white mb-1">
{data.weight_name}
</h6>
<p className="fs-7 opacity-75">{timeAgo}</p>
<div className="mt-2" onClick={handleInnerClick}>
<LikeButton
onToggle={handleLike}
likeCount={data.likes}
overlay={true}
/>
<div className="card-img-overlay-text">
<div>
<h6 className="fw-semibold text-white mb-1">
{data.weight_name}
</h6>
<p className="fs-7 opacity-75">{timeAgo}</p>
<div className="mt-2" onClick={handleInnerClick}>
<LikeButton
onToggle={handleLike}
likeCount={data.likes}
overlay={true}
/>
</div>
</div>
</div>
</div>
</div>
</>
)}
</Card>
</>
)}
</Card>
</Link>
);
}
@@ -1 +0,0 @@
export { default } from "./FavoriteButton";
@@ -5,15 +5,17 @@
align-items: center;
justify-content: center;
background-color: rgba(lighten($secondary, 5%), 0.9);
color: inherit;
color: rgba($white, 0.7);
border: none;
padding: 0;
font: inherit;
font-size: 14px;
cursor: pointer;
outline: inherit;
font-size: 16px;
transition: 0.2s;
padding: 3px 8px;
min-width: 52px;
border-radius: 0.5rem;
&.shadow {
@@ -29,10 +29,11 @@ export default function LikeButton({
const [isToggled, setIsToggled] = useState(initialToggled);
const [isLoading, setIsLoading] = useState(false);
const handleClick = async () => {
const handleClick = async (event: any) => {
event.preventDefault();
event.stopPropagation();
setIsLoading(true);
onToggle(entityToken, entityType)
.then((isToggled: boolean) => {
onToggle(entityToken, entityType).then((isToggled: boolean) => {
setIsToggled(isToggled);
setIsLoading(false);
});
@@ -64,7 +65,7 @@ export default function LikeButton({
placement="bottom"
>
<button
// onClick={handleClick} // unnecessary, parent has onClick, runs twice
// onClick={handleClick} // unnecessary, parent has onClick, runs twice
disabled={isLoading}
className={`${buttonClass} ${buttonShadow} ${large ? "large" : ""}`}
>
@@ -25,7 +25,7 @@ import {
} from "@storyteller/components/src/jobs/InferenceJob";
import { TtsInferenceJob } from "@storyteller/components/src/jobs/TtsInferenceJobs";
import Badge from "components/common/Badge";
import FavoriteButton from "components/common/FavoriteButton";
import BookmarkButton from "components/common/BookmarkButton";
import LikeButton from "components/common/LikeButton";
import VdInferencePanel from "./inference_panels/VdInferencePanel";
import VcInferencePanel from "./inference_panels/VcInferencePanel";
@@ -380,7 +380,7 @@ export default function WeightPage({
large: true,
}}
/>
<FavoriteButton
<BookmarkButton
{...{
entityToken: weight_token,
entityType: "model_weight",