mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-06 02:00:46 +00:00
initial ratings/likes progress
This commit is contained in:
1 parent
d0018ea4e8
commit
b64fc69d83
11 files changed
+156
-178
No files matched your search
@@ -0,0 +1,23 @@
|
||||
import MakeRequest from "../MakeRequest";
|
||||
|
||||
export interface Rating {
|
||||
entity_token: string,
|
||||
entity_type: string,
|
||||
rating_value: string
|
||||
}
|
||||
|
||||
export interface GetRatingsRequest {}
|
||||
|
||||
export interface GetRatingsResponse {
|
||||
success: boolean,
|
||||
ratings?: Rating[]
|
||||
}
|
||||
|
||||
export interface GetRatingsQueries {
|
||||
tokens: string[],
|
||||
}
|
||||
|
||||
export const GetRatings = MakeRequest<string, GetRatingsRequest, GetRatingsResponse, GetRatingsQueries>({
|
||||
method: "GET",
|
||||
routingFunction: () => `/v1/user_rating/batch`,
|
||||
});
|
||||
@@ -17,18 +17,20 @@ interface AudioCardProps {
|
||||
bookmarks: any;
|
||||
data: any;
|
||||
origin?: string;
|
||||
type: "media" | "weights";
|
||||
ratings: any;
|
||||
showCreator?: boolean;
|
||||
showCover?: boolean;
|
||||
type: "media" | "weights";
|
||||
}
|
||||
|
||||
export default function AudioCard({
|
||||
bookmarks,
|
||||
data,
|
||||
origin = "",
|
||||
type,
|
||||
ratings,
|
||||
showCreator,
|
||||
showCover,
|
||||
type,
|
||||
}: AudioCardProps) {
|
||||
const linkUrl =
|
||||
type === "media"
|
||||
@@ -43,11 +45,6 @@ export default function AudioCard({
|
||||
|
||||
const timeAgo = useTimeAgo(data.created_at);
|
||||
|
||||
const handleLike = async () => {
|
||||
console.log(`The item is now ${data.isLiked ? "liked" : "not liked"}.`);
|
||||
return true; // temporary, replace with like function
|
||||
};
|
||||
|
||||
const { label: weightBadgeLabel, color: weightBadgeColor } =
|
||||
useWeightTypeInfo(
|
||||
data.weight_type || data.details?.maybe_weight_data?.weight_type
|
||||
@@ -126,7 +123,12 @@ export default function AudioCard({
|
||||
)}
|
||||
|
||||
<div>
|
||||
<LikeButton onToggle={handleLike} likeCount={data.like || 0} />
|
||||
<LikeButton {...{
|
||||
entityToken: data.token,
|
||||
entityType: "media_file",
|
||||
likeCount: data.likes,
|
||||
onToggle: ratings.toggle
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
@@ -184,7 +186,12 @@ export default function AudioCard({
|
||||
)}
|
||||
|
||||
<div>
|
||||
<LikeButton onToggle={handleLike} likeCount={data.likes} />
|
||||
<LikeButton {...{
|
||||
entityToken: data.weight_token,
|
||||
entityType: "model_weight",
|
||||
likeCount: data.likes,
|
||||
onToggle: ratings.toggle
|
||||
}} />
|
||||
</div>
|
||||
<BookmarkButton
|
||||
{...{
|
||||
|
||||
@@ -15,16 +15,18 @@ interface ImageCardProps {
|
||||
bookmarks: any;
|
||||
data: any;
|
||||
origin?: string;
|
||||
type: "media" | "weights";
|
||||
ratings: any;
|
||||
showCreator?: boolean;
|
||||
type: "media" | "weights";
|
||||
}
|
||||
|
||||
export default function ImageCard({
|
||||
bookmarks,
|
||||
data,
|
||||
origin = "",
|
||||
type,
|
||||
showCreator,
|
||||
ratings,
|
||||
type
|
||||
}: ImageCardProps) {
|
||||
const linkUrl =
|
||||
type === "media"
|
||||
@@ -39,11 +41,6 @@ export default function ImageCard({
|
||||
|
||||
const timeAgo = useTimeAgo(data.created_at);
|
||||
|
||||
const handleLike = async () => {
|
||||
console.log(`The item is now ${data.isLiked ? "liked" : "not liked"}.`);
|
||||
return true; // temporary will be replaced with like function
|
||||
};
|
||||
|
||||
const { label: weightBadgeLabel, color: weightBadgeColor } =
|
||||
useWeightTypeInfo(
|
||||
data.weight_type || data.details?.maybe_weight_data?.weight_type
|
||||
@@ -122,7 +119,12 @@ export default function ImageCard({
|
||||
)}
|
||||
|
||||
<div>
|
||||
<LikeButton onToggle={handleLike} likeCount={data.likes} />
|
||||
<LikeButton {...{
|
||||
entityToken: data.token,
|
||||
entityType: "media_file",
|
||||
likeCount: data.likes,
|
||||
onToggle: ratings.toggle
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -186,7 +188,12 @@ export default function ImageCard({
|
||||
)}
|
||||
|
||||
<div>
|
||||
<LikeButton onToggle={handleLike} likeCount={data.likes} />
|
||||
<LikeButton {...{
|
||||
entityToken: data.weight_token,
|
||||
entityType: "model_weight",
|
||||
likeCount: data.likes,
|
||||
onToggle: ratings.toggle
|
||||
}} />
|
||||
</div>
|
||||
<BookmarkButton
|
||||
{...{
|
||||
|
||||
@@ -13,11 +13,12 @@ import { Link } from "react-router-dom";
|
||||
interface VideoCardProps {
|
||||
data: any;
|
||||
origin?: string;
|
||||
type: "media" | "weights";
|
||||
ratings: any;
|
||||
showCreator?: boolean;
|
||||
type: "media" | "weights";
|
||||
}
|
||||
|
||||
export default function VideoCard({ data, origin = "", type, showCreator }: VideoCardProps) {
|
||||
export default function VideoCard({ data, origin = "", ratings, showCreator, type }: VideoCardProps) {
|
||||
const linkUrl =
|
||||
type === "media" ? `/media/${data.token}` : `/weight/${data.weight_token}`;
|
||||
|
||||
@@ -27,11 +28,6 @@ export default function VideoCard({ data, origin = "", type, showCreator }: Vide
|
||||
|
||||
const timeAgo = useTimeAgo(data.created_at);
|
||||
|
||||
const handleLike = async () => {
|
||||
console.log(`The item is now ${data.isLiked ? "liked" : "not liked"}.`);
|
||||
return true; // temporary, replace with like function
|
||||
};
|
||||
|
||||
const videoLink = new BucketConfig().getGcsUrl(data.public_bucket_path);
|
||||
|
||||
return (
|
||||
@@ -86,10 +82,12 @@ export default function VideoCard({ data, origin = "", type, showCreator }: Vide
|
||||
)}
|
||||
|
||||
<div>
|
||||
<LikeButton
|
||||
onToggle={handleLike}
|
||||
likeCount={data.likes}
|
||||
/>
|
||||
<LikeButton {...{
|
||||
entityToken: data.token,
|
||||
entityType: "media_file",
|
||||
likeCount: data.likes,
|
||||
onToggle: ratings.toggle
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -132,11 +130,13 @@ export default function VideoCard({ data, origin = "", type, showCreator }: Vide
|
||||
</h6>
|
||||
<p className="fs-7 opacity-75">{timeAgo}</p>
|
||||
<div className="mt-2" onClick={handleInnerClick}>
|
||||
<LikeButton
|
||||
onToggle={handleLike}
|
||||
likeCount={data.likes}
|
||||
overlay={true}
|
||||
/>
|
||||
<LikeButton {...{
|
||||
entityToken: data.weight_token,
|
||||
entityType: "model_weight",
|
||||
likeCount: data.likes,
|
||||
onToggle: ratings.toggle,
|
||||
overlay: true
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -26,15 +26,16 @@ export default function LikeButton({
|
||||
overlay,
|
||||
large,
|
||||
}: LikeButtonProps) {
|
||||
const [isToggled, setIsToggled] = useState(initialToggled);
|
||||
const isToggled = initialToggled; // toggled value managed externally via useRatings, this may change
|
||||
// const [isToggled, setIsToggled] = useState(initialToggled);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleClick = async (event: React.MouseEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setIsLoading(true);
|
||||
// setIsLoading(true);
|
||||
onToggle(entityToken, entityType).then((isToggled: boolean) => {
|
||||
setIsToggled(isToggled);
|
||||
// setIsToggled(isToggled);
|
||||
setIsLoading(false);
|
||||
});
|
||||
// try {
|
||||
|
||||
@@ -1,11 +1,46 @@
|
||||
import { useState } from 'react';
|
||||
import { FetchStatus } from "@storyteller/components/src/api/_common/SharedFetchTypes";
|
||||
import { GetRatings, GetRatingsResponse } from "@storyteller/components/src/api/user_ratings/GetRatings";
|
||||
import { SetRating, SetRatingResponse } from "@storyteller/components/src/api/user_ratings/SetRating";
|
||||
|
||||
interface Props {
|
||||
value?: any;
|
||||
}
|
||||
// interface Props {
|
||||
// value?: any
|
||||
// }
|
||||
|
||||
export default function useRatings({ value }: Props) {
|
||||
const [status, statusSet] = useState(FetchStatus.ready);
|
||||
return { status, statusSet };
|
||||
export default function useRatings() {
|
||||
// const [library, librarySet] = useState({});
|
||||
const [baseList, baseListSet] = useState<any[]>([]);
|
||||
const [toggleStatus, toggleStatusSet] = useState(FetchStatus.ready);
|
||||
|
||||
const library = baseList.reduce((obj: any, current: any) => {
|
||||
return { ...obj, [current.entity_token]: current.token }
|
||||
},{});
|
||||
|
||||
const gather = (res: any) => {
|
||||
let tokens = res.results.map((item: any) => item.token);
|
||||
console.log("💧", tokens);
|
||||
GetRatings("",{},{ tokens }).then((res: GetRatingsResponse) => {
|
||||
if (res.success && res.ratings) {
|
||||
console.log("🪙",res);
|
||||
baseListSet(res.ratings);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const toggle = (token: string, entityType: string) => {
|
||||
let inLibrary = library[token];
|
||||
|
||||
toggleStatusSet(FetchStatus.in_progress);
|
||||
console.log("⏳ toggling bookmark ...",{ token, entityType });
|
||||
SetRating("",{
|
||||
entity_token: token,
|
||||
entity_type: entityType,
|
||||
rating_value: !inLibrary || inLibrary.rating_value === "neutral" ? "positive" : "neutral"
|
||||
}).then(( res: SetRatingResponse ) => {
|
||||
toggleStatusSet(FetchStatus.ready);
|
||||
console.log("👍 like toggled",res);
|
||||
});
|
||||
};
|
||||
|
||||
return { gather, toggleStatus, toggleStatusSet, toggle };
|
||||
};
|
||||
@@ -1,20 +1,18 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import MasonryGrid from "components/common/MasonryGrid/MasonryGrid";
|
||||
import AudioCard from "components/common/Card/AudioCard";
|
||||
|
||||
import MediaCards from "components/common/Card/MediaCards";
|
||||
import { Link } from "react-router-dom";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faChevronRight } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia";
|
||||
import { useBookmarks } from "hooks";
|
||||
import { useBookmarks, useRatings } from "hooks";
|
||||
import { ListFeaturedMediaFiles } from "@storyteller/components/src/api/media_files/ListFeaturedMediaFiles";
|
||||
import { FetchStatus } from "@storyteller/components/src/api/_common/SharedFetchTypes";
|
||||
import SkeletonCard from "components/common/Card/SkeletonCard";
|
||||
import ImageCard from "components/common/Card/ImageCard";
|
||||
import VideoCard from "components/common/Card/VideoCard";
|
||||
|
||||
export default function FeaturedTab() {
|
||||
const bookmarks = useBookmarks();
|
||||
const ratings = useRatings();
|
||||
const gridContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
const [list, listSet] = useState<MediaFile[]>([]);
|
||||
// const [showMasonryGrid, setShowMasonryGrid] = useState(true);
|
||||
@@ -61,57 +59,16 @@ export default function FeaturedTab() {
|
||||
gridRef={gridContainerRef}
|
||||
onLayoutComplete={() => console.log("Layout complete!")}
|
||||
>
|
||||
{ list.map((data: any, index: number) => {
|
||||
let card;
|
||||
switch (data.media_type) {
|
||||
case "audio":
|
||||
card = (
|
||||
<AudioCard
|
||||
{...{
|
||||
bookmarks,
|
||||
data,
|
||||
type: "media",
|
||||
showCreator: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case "image":
|
||||
card = (
|
||||
<ImageCard
|
||||
{...{
|
||||
bookmarks,
|
||||
data,
|
||||
type: "media",
|
||||
showCreator: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case "video":
|
||||
card = (
|
||||
<VideoCard
|
||||
{...{
|
||||
bookmarks,
|
||||
data,
|
||||
type: "media",
|
||||
showCreator: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
card = <div>Unsupported media type</div>;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="col-12 col-sm-6 col-xl-4 grid-item"
|
||||
>
|
||||
{card}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{ list.map((data: any, key: number) => {
|
||||
let props = { bookmarks, data, origin, ratings, type: "media" };
|
||||
|
||||
return <div {...{
|
||||
className: "col-12 col-sm-6 col-xl-4 grid-item",
|
||||
key
|
||||
}}>
|
||||
<MediaCards {...{ type: data.media_type, props }} />
|
||||
</div>;
|
||||
}) }
|
||||
</MasonryGrid>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import MasonryGrid from "components/common/MasonryGrid/MasonryGrid";
|
||||
import AudioCard from "components/common/Card/AudioCard";
|
||||
import ImageCard from "components/common/Card/ImageCard";
|
||||
import VideoCard from "components/common/Card/VideoCard";
|
||||
import MediaCards from "components/common/Card/MediaCards";
|
||||
import { TempSelect } from "components/common";
|
||||
import {
|
||||
faArrowDownWideShort,
|
||||
@@ -13,7 +11,7 @@ import AudioPlayerProvider from "components/common/AudioPlayer/AudioPlayerContex
|
||||
import SkeletonCard from "components/common/Card/SkeletonCard";
|
||||
import { ListMediaFiles } from "@storyteller/components/src/api/media_files/ListMediaFiles";
|
||||
import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia";
|
||||
import { useBookmarks, useLazyLists } from "hooks";
|
||||
import { useBookmarks, useLazyLists, useRatings } from "hooks";
|
||||
import InfiniteScroll from "react-infinite-scroll-component";
|
||||
import prepFilter from "resources/prepFilter";
|
||||
|
||||
@@ -21,6 +19,7 @@ export default function MediaTab() {
|
||||
const { search } = useLocation();
|
||||
const urlQueries = new URLSearchParams(search);
|
||||
const bookmarks = useBookmarks();
|
||||
const ratings = useRatings();
|
||||
const gridContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
const [weightType, weightTypeSet] = useState(
|
||||
urlQueries.get("maybe_scoped_weight_type") || "all"
|
||||
@@ -116,58 +115,16 @@ export default function MediaTab() {
|
||||
<MasonryGrid
|
||||
gridRef={gridContainerRef}
|
||||
onLayoutComplete={() => console.log("Layout complete!")}
|
||||
>
|
||||
{media.list.map((data: any, index: number) => {
|
||||
let card;
|
||||
switch (data.media_type) {
|
||||
case "audio":
|
||||
card = (
|
||||
<AudioCard
|
||||
{...{
|
||||
bookmarks,
|
||||
data,
|
||||
type: "media",
|
||||
showCreator: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case "image":
|
||||
card = (
|
||||
<ImageCard
|
||||
{...{
|
||||
bookmarks,
|
||||
data,
|
||||
type: "media",
|
||||
showCreator: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case "video":
|
||||
card = (
|
||||
<VideoCard
|
||||
{...{
|
||||
bookmarks,
|
||||
data,
|
||||
type: "media",
|
||||
showCreator: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
card = <div>Unsupported media type</div>;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="col-12 col-sm-6 col-xl-4 grid-item"
|
||||
>
|
||||
{card}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
> { media.list.map((data: any, key: number) => {
|
||||
let props = { bookmarks, data, origin, ratings, type: "media" };
|
||||
|
||||
return <div {...{
|
||||
className: "col-12 col-sm-6 col-xl-4 grid-item",
|
||||
key
|
||||
}}>
|
||||
<MediaCards {...{ type: data.media_type, props }} />
|
||||
</div>;
|
||||
}) }
|
||||
</MasonryGrid>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import Pagination from "components/common/Pagination";
|
||||
|
||||
import { useBookmarks, useListContent } from "hooks";
|
||||
import { useBookmarks, useListContent, useRatings } from "hooks";
|
||||
import { GetBookmarksByUser } from "@storyteller/components/src/api/bookmarks/GetBookmarksByUser";
|
||||
import WeightsCards from "components/common/Card/WeightsCards";
|
||||
import prepFilter from "resources/prepFilter";
|
||||
@@ -18,6 +18,7 @@ export default function BookmarksTab({ username }: { username: string }) {
|
||||
const { pathname: origin, search } = useLocation();
|
||||
const urlQueries = new URLSearchParams(search);
|
||||
const bookmarks = useBookmarks();
|
||||
const ratings = useRatings();
|
||||
const gridContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
const [showMasonryGrid, setShowMasonryGrid] = useState(true);
|
||||
const [weightType, weightTypeSet] = useState(
|
||||
@@ -205,13 +206,7 @@ export default function BookmarksTab({ username }: { username: string }) {
|
||||
onLayoutComplete={() => console.log("Layout complete!")}
|
||||
>
|
||||
{dataList.map((data: any, key: number) => {
|
||||
let weightProps = {
|
||||
bookmarks,
|
||||
data,
|
||||
origin,
|
||||
type: "weights",
|
||||
showCreator: true,
|
||||
};
|
||||
let weightProps = { bookmarks, data, origin, ratings, showCreator: true, type: "weights" };
|
||||
|
||||
// let mediaProps = {
|
||||
// bookmarks,
|
||||
|
||||
@@ -13,34 +13,35 @@ import Pagination from "components/common/Pagination";
|
||||
|
||||
import { GetMediaByUser } from "@storyteller/components/src/api/media_files/GetMediaByUser";
|
||||
import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia";
|
||||
import { useBookmarks, useListContent } from "hooks";
|
||||
import { useBookmarks, useListContent, useRatings } from "hooks";
|
||||
import prepFilter from "resources/prepFilter";
|
||||
|
||||
export default function MediaTab({ username }: { username: string }) {
|
||||
const { pathname: origin, search } = useLocation();
|
||||
const urlQueries = new URLSearchParams(search);
|
||||
const bookmarks = useBookmarks();
|
||||
const ratings = useRatings();
|
||||
const gridContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
const [showMasonryGrid, setShowMasonryGrid] = useState(true);
|
||||
const [mediaType, mediaTypeSet] = useState(
|
||||
urlQueries.get("filter_media_type") || "all"
|
||||
);
|
||||
const [list, listSet] = useState<MediaFile[]>([]);
|
||||
// const resetMasonryGrid = () => {
|
||||
// setShowMasonryGrid(false);
|
||||
// setTimeout(() => setShowMasonryGrid(true), 10);
|
||||
// };
|
||||
const media = useListContent({
|
||||
addQueries: {
|
||||
page_size: 24,
|
||||
...prepFilter(mediaType, "filter_media_type"),
|
||||
},
|
||||
addSetters: { mediaTypeSet },
|
||||
debug: "profile media",
|
||||
fetcher: GetMediaByUser,
|
||||
list,
|
||||
listSet,
|
||||
onInputChange: () => setShowMasonryGrid(false),
|
||||
onSuccess: () => setShowMasonryGrid(true),
|
||||
onSuccess: (res) => {
|
||||
ratings.gather(res);
|
||||
setShowMasonryGrid(true);
|
||||
},
|
||||
requestList: true,
|
||||
urlParam: username,
|
||||
});
|
||||
@@ -114,7 +115,7 @@ export default function MediaTab({ username }: { username: string }) {
|
||||
onLayoutComplete={() => console.log("Layout complete!")}
|
||||
>
|
||||
{media.list.map((data: MediaFile, key: number) => {
|
||||
let props = { bookmarks, data, origin, type: "media" };
|
||||
let props = { bookmarks, data, origin, ratings, type: "media" };
|
||||
return (
|
||||
<div
|
||||
{...{
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
faFilter,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import Pagination from "components/common/Pagination";
|
||||
import { useBookmarks, useListContent } from "hooks";
|
||||
import { useBookmarks, useListContent, useRatings } from "hooks";
|
||||
import { GetWeightsByUser } from "@storyteller/components/src/api/weights/GetWeightsByUser";
|
||||
import { TempSelect } from "components/common";
|
||||
import SkeletonCard from "components/common/Card/SkeletonCard";
|
||||
@@ -34,6 +34,7 @@ export default function WeightsTab({ username }: { username: string }) {
|
||||
const [vc, vcSet] = useState("all");
|
||||
const [showMasonryGrid, setShowMasonryGrid] = useState(true);
|
||||
const bookmarks = useBookmarks();
|
||||
const ratings = useRatings();
|
||||
const [list, listSet] = useState<any[]>([]);
|
||||
const weights = useListContent({
|
||||
addQueries: {
|
||||
@@ -193,13 +194,7 @@ export default function WeightsTab({ username }: { username: string }) {
|
||||
onLayoutComplete={() => console.log("Layout complete!")}
|
||||
>
|
||||
{weights.list.map((data: any, key: number) => {
|
||||
let props = {
|
||||
bookmarks,
|
||||
data,
|
||||
origin,
|
||||
showCreator: true,
|
||||
type: "weights",
|
||||
};
|
||||
let props = { bookmarks, data, origin, ratings, showCreator: true, type: "weights" };
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
Reference in new issue
Block a user