diff --git a/packages/components/src/api/MakeRequest.ts b/packages/components/src/api/MakeRequest.ts index 35287e3d0c..1c70cd2005 100644 --- a/packages/components/src/api/MakeRequest.ts +++ b/packages/components/src/api/MakeRequest.ts @@ -25,12 +25,11 @@ const METHOD_OMITS_BODY: { [key: string]: boolean } = { const MakeRequest = (routeSetup: RouteSetup) : (urlRouteArgs: UrlRouteArgs, request: Request, queries?: UrlParams) => Promise => { return async function(urlRouteArgs: UrlRouteArgs, request: Request, queries?: any) : Promise { - const prepedQueries = queries ? Object.keys(queries).reduce((obj,key) => { - let current = queries[key] - return { [key]: Array.isArray(current) ? current.join("&tokens=") : current }; - },{}) : null; - console.log("❓",prepedQueries); - const endpoint = `${ routeSetup.routingFunction(urlRouteArgs) }${ prepedQueries ? "?" + new URLSearchParams(prepedQueries) : "" }`; + const newQueries = queries ? Object.keys(queries).map((key, i) => { + return `${ i ? "&" : "" }${ key }=${ Array.isArray(queries[key]) ? queries[key].join(`&${ key }=`) : queries[key] }` + }).join("") : null; + + const endpoint = `${ routeSetup.routingFunction(urlRouteArgs) }${ newQueries ? "?" + newQueries : "" }`; const method = routeSetup.method; const methodOmitsBody = METHOD_OMITS_BODY[method] || false; diff --git a/packages/components/src/api/bookmarks/GetBookmarks.ts b/packages/components/src/api/bookmarks/GetBookmarks.ts new file mode 100644 index 0000000000..485ffd21c6 --- /dev/null +++ b/packages/components/src/api/bookmarks/GetBookmarks.ts @@ -0,0 +1,24 @@ +import MakeRequest from "../MakeRequest"; + +export interface Bookmark { + entity_token: string, + entity_type: string, + is_bookmarked: boolean, + maybe_bookmark_token?: string +} + +export interface GetBookmarksRequest {} + +export interface GetBookmarksResponse { + success: boolean, + bookmarks?: Bookmark[] +} + +export interface GetBookmarksQueries { + tokens: string[], +} + +export const GetBookmarks = MakeRequest({ + method: "GET", + routingFunction: () => `/v1/user_bookmarks/batch`, +}); \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/common/BookmarkButton/BookmarkButton.scss b/packages/fakeyou.com/src/components/common/BookmarkButton/BookmarkButton.scss index 4524647d81..3f16a88003 100644 --- a/packages/fakeyou.com/src/components/common/BookmarkButton/BookmarkButton.scss +++ b/packages/fakeyou.com/src/components/common/BookmarkButton/BookmarkButton.scss @@ -13,10 +13,15 @@ cursor: pointer; outline: inherit; font-size: 16px; - transition: 0.2s; + transition: 0.2s all; padding: 3px 8px; - min-width: 52px; + flex-wrap: none; + overflow: hidden; + width: fit-content; + height: 1.75rem; border-radius: 0.5rem; + // white-space: nowrap; + position: relative; &.shadow { box-shadow: 0 0 12px rgba($bg, 0.4) !important; @@ -74,8 +79,55 @@ color: $white; font-size: 14px; font-weight: 400 !important; + height: 100%; opacity: 0.9; transition: 0.2s; + // flex-grow: 1; + position: relative; + // width: 1.5rem; + // display: inline-block; + // width: 100%; + // height: max-content; + + .favorite-text-wrapper { + position: relative; + } + + span { + display: block; + top: 0; + // height: fit-content; + // width: 2.5rem; + background-color: green; + position: absolute; + } +} + +@keyframes workdots { + 0% { opacity: .33 } + 33% { opacity: .66 } + 66% { opacity: .66 } + 100% { opacity: .33 } +} + +.fy-workdots { + width: 1rem; + height: 1rem; + fill: #fff; + + circle { + animation: .66s infinite workdots; + animation-timing-function: ease-in-out; + } + :nth-child(1) { + animation-delay: 0; + } + :nth-child(2) { + animation-delay: .22s; + } + :nth-child(3) { + animation-delay: .44s; + } } @keyframes pulse { diff --git a/packages/fakeyou.com/src/components/common/BookmarkButton/BookmarkButton.tsx b/packages/fakeyou.com/src/components/common/BookmarkButton/BookmarkButton.tsx index e4d54583b9..8dc17acb4b 100644 --- a/packages/fakeyou.com/src/components/common/BookmarkButton/BookmarkButton.tsx +++ b/packages/fakeyou.com/src/components/common/BookmarkButton/BookmarkButton.tsx @@ -2,12 +2,15 @@ import React, { useState } from "react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faBookmark } from "@fortawesome/pro-solid-svg-icons"; import { faBookmark as faBookmarkOutline } from "@fortawesome/pro-regular-svg-icons"; +import { a, useTransition } from "@react-spring/web"; +import { basicTransition } from "resources"; import Tippy from "@tippyjs/react"; import "tippy.js/dist/tippy.css"; import "./BookmarkButton.scss"; // import useShortenNumber from "hooks/useShortenNumber"; interface BookmarkButtonProps { + busy?: boolean; entityToken?: string; entityType?: string; initialToggled?: boolean; @@ -18,6 +21,7 @@ interface BookmarkButtonProps { } export default function BookmarkButton({ + busy, entityToken = "", entityType = "", initialToggled = false, @@ -29,6 +33,7 @@ export default function BookmarkButton({ const isToggled = initialToggled; // toggled value managed externally via usebookmarks, this may change // const [isToggled, setIsToggled] = useState(initialToggled); const [isLoading, setIsLoading] = useState(false); + const [animating,animatingSet] = useState(false); const handleClick = async (event: React.MouseEvent) => { event.preventDefault(); @@ -54,6 +59,15 @@ export default function BookmarkButton({ const toolTip = isToggled ? "Unbookmark" : "Bookmark"; // let favoriteCountShort = useShortenNumber(favoriteCount || 0); + const index = busy ? 0 : isToggled ? 1 : 2; + + const transitions = useTransition(index, basicTransition({ + onRest: () => animatingSet(false), + onStart: () => { + animatingSet(true) + } + })); + return (
-

{isToggled ? "Saved" : "Save"}

+
+
+ { + transitions((style, i, state) => { + let isLeaving = state.phase === "leave"; + const content = (txt = "") => + { + txt ? txt : + + + + + }; + return [ content(""), content("Saved"), content("Save") + ][i]; + }) + } +
+
diff --git a/packages/fakeyou.com/src/components/common/Card/AudioCard/AudioCard.tsx b/packages/fakeyou.com/src/components/common/Card/AudioCard/AudioCard.tsx index 57070db90f..00acfa2c2a 100644 --- a/packages/fakeyou.com/src/components/common/Card/AudioCard/AudioCard.tsx +++ b/packages/fakeyou.com/src/components/common/Card/AudioCard/AudioCard.tsx @@ -195,10 +195,11 @@ export default function AudioCard({ diff --git a/packages/fakeyou.com/src/components/common/Card/ImageCard/ImageCard.tsx b/packages/fakeyou.com/src/components/common/Card/ImageCard/ImageCard.tsx index 935b32721c..7b14cef0e8 100644 --- a/packages/fakeyou.com/src/components/common/Card/ImageCard/ImageCard.tsx +++ b/packages/fakeyou.com/src/components/common/Card/ImageCard/ImageCard.tsx @@ -199,10 +199,11 @@ export default function ImageCard({ diff --git a/packages/fakeyou.com/src/hooks/useBookmarks/useBookmarks.tsx b/packages/fakeyou.com/src/hooks/useBookmarks/useBookmarks.tsx index b44b29fe1d..c68f45b0dd 100644 --- a/packages/fakeyou.com/src/hooks/useBookmarks/useBookmarks.tsx +++ b/packages/fakeyou.com/src/hooks/useBookmarks/useBookmarks.tsx @@ -1,59 +1,93 @@ -import { useEffect, useState } from 'react'; +import { useState } from 'react'; +import { GetBookmarks, GetBookmarksResponse } from "@storyteller/components/src/api/bookmarks/GetBookmarks"; import { CreateBookmark } from "@storyteller/components/src/api/bookmarks/CreateBookmark"; import { DeleteBookmark } from "@storyteller/components/src/api/bookmarks/DeleteBookmark"; -import { GetBookmarksByUser } from "@storyteller/components/src/api/bookmarks/GetBookmarksByUser"; import { FetchStatus } from "@storyteller/components/src/api/_common/SharedFetchTypes"; -import { useSession } from "hooks"; +// import { useSession } from "hooks"; + +interface BookmarkLibrary { + [key: string]: any +} export default function useBookmarks() { - const { user } = useSession(); - const [baseList, baseListSet] = useState([]); + const [list, listSet] = useState({}); + const [busyList, busyListSet] = useState({}); const [status, statusSet] = useState(FetchStatus.ready); - const list = baseList.reduce((obj: any, current: any) => { - return { ...obj, [current.details.entity_token]: current.token } - },{}); + const getBookmarks = (tokens = [], expand = false) => { + busyListSet(tokens.reduce((obj,token) => ({ ...obj, [token]: true }),{})); + GetBookmarks("",{},{ tokens }).then((res: GetBookmarksResponse) => { + if (res.success && res.bookmarks) { + let newBatch = res.bookmarks.reduce((obj, { entity_token, ...bookmark }) => ({ + ...obj, + [entity_token]: bookmark + }),{}); + busyListSet({}); // this should be a for each key in tokens delete from busyList, but this is fine for now + listSet(list => expand ? { ...list, ...newBatch } : newBatch); + } + }) + }; - const toggle = (entityToken = "", type = "") => { - let bookmarkToken = list[entityToken]; + const gather = ({ res, expand }: { res: any, expand?: boolean }) => { + let tokens = res.results.map((item: any) => item.weight_token); + getBookmarks(tokens, expand); + }; + + const toggleList = (entity_type = "", entityToken = "", maybe_bookmark_token = null) => (list: BookmarkLibrary) => ({ + ...list, + [entityToken]: { + entity_type, + is_bookmarked: !!maybe_bookmark_token, + maybe_bookmark_token + } + }); + + // console.log("😎",list); + + const toggle = (entityToken: string, type: string) => { + statusSet(FetchStatus.in_progress); + busyListSet(state => ({ ...state, [entityToken]: true })); + let bookmarkToken = list[entityToken].maybe_bookmark_token; if (bookmarkToken) { - console.log("⏳ deleting bookmark",); + console.log(`⏳ deleting bookmark: ${ entityToken }`,bookmarkToken); return DeleteBookmark(bookmarkToken,{ as_mod: true }) .then((res: any) => { console.log("🔥",res); + busyListSet(state => { + let newState = { ...state }; + delete newState[entityToken]; + return newState; + }); + listSet(toggleList(type,entityToken)); statusSet(FetchStatus.ready); return false; }); } else { - console.log("⏳ creating bookmark",); + console.log(`⏳ creating bookmark: ${ entityToken }`,bookmarkToken); return CreateBookmark("",{ entity_token: entityToken, entity_type: type }) .then((res: any) => { console.log("🔖",res); + busyListSet(state => { + let newState = { ...state }; + delete newState[entityToken]; + return newState; + }); + listSet(toggleList(type,entityToken,res.user_bookmark_token)); statusSet(FetchStatus.ready); return true; }); } }; - useEffect(() => { - if (user && status === FetchStatus.ready) { - statusSet(FetchStatus.in_progress); - GetBookmarksByUser(user?.username || "",{},{ page_size: 999 }) // high number because we want all bookmarks - .then((res: any) => { - statusSet(FetchStatus.success); - if (res.results) { - baseListSet(res.results); - } - }); - } - },[status, user]); - return { + busyList, + gather, list, + status, toggle }; }; \ No newline at end of file diff --git a/packages/fakeyou.com/src/resources/basicTransition.ts b/packages/fakeyou.com/src/resources/basicTransition.ts index e30dbc2b69..87c52112a6 100644 --- a/packages/fakeyou.com/src/resources/basicTransition.ts +++ b/packages/fakeyou.com/src/resources/basicTransition.ts @@ -1,10 +1,13 @@ import { config } from "@react-spring/web"; +const n = (x:any) => {}; -const basicTransition = ({ ...overwrite }) => ({ +const basicTransition = ({ ...overwrite }, onRest = n, onStart = n) => ({ config: config.gentle, - from: { opacity: 0 }, - enter: { opacity: 1 }, - leave: { opacity: 0 }, + from: { opacity: 0, }, + enter: { opacity: 1, }, + leave: { opacity: 0, }, + onRest, + onStart, ...overwrite }); diff --git a/packages/fakeyou.com/src/v2/view/pages/explore/tabs/MediaTab.tsx b/packages/fakeyou.com/src/v2/view/pages/explore/tabs/MediaTab.tsx index a3c7526e99..2a707edd5a 100644 --- a/packages/fakeyou.com/src/v2/view/pages/explore/tabs/MediaTab.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/explore/tabs/MediaTab.tsx @@ -37,7 +37,10 @@ export default function MediaTab() { list, listSet, onInputChange: () => setShowMasonryGrid(false), - onSuccess: () => setShowMasonryGrid(true), + onSuccess: (res) => { + bookmarks.gather({ res, expand: true }); // expand rather than replace for lazy loading + setShowMasonryGrid(true); + }, requestList: true, }); diff --git a/packages/fakeyou.com/src/v2/view/pages/explore/tabs/WeightsTab.tsx b/packages/fakeyou.com/src/v2/view/pages/explore/tabs/WeightsTab.tsx index e5771cb827..5a87352439 100644 --- a/packages/fakeyou.com/src/v2/view/pages/explore/tabs/WeightsTab.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/explore/tabs/WeightsTab.tsx @@ -30,11 +30,15 @@ export default function WeightsTab() { ...prepFilter(weightType, "maybe_scoped_weight_type"), }, addSetters: { weightCategorySet, weightTypeSet }, + debug: "explore weights tab", fetcher: ListWeights, list, listSet, onInputChange: () => setShowMasonryGrid(false), - onSuccess: () => setShowMasonryGrid(true), + onSuccess: (res) => { + bookmarks.gather({ res, expand: true }); // expand rather than replace for lazy loading + setShowMasonryGrid(true); + }, requestList: true, }); diff --git a/packages/fakeyou.com/src/v2/view/pages/profile/profile_view/tabs/BookmarksTab.tsx b/packages/fakeyou.com/src/v2/view/pages/profile/profile_view/tabs/BookmarksTab.tsx index 9217589b1e..3e4d321976 100644 --- a/packages/fakeyou.com/src/v2/view/pages/profile/profile_view/tabs/BookmarksTab.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/profile/profile_view/tabs/BookmarksTab.tsx @@ -57,7 +57,10 @@ export default function BookmarksTab({ username }: { username: string }) { list, listSet, onInputChange: () => setShowMasonryGrid(false), - onSuccess: () => setShowMasonryGrid(true), + onSuccess: (res) => { + bookmarks.gather({ res }); + setShowMasonryGrid(true); + }, requestList: true, urlParam: username, }); diff --git a/packages/fakeyou.com/src/v2/view/pages/profile/profile_view/tabs/MediaTab.tsx b/packages/fakeyou.com/src/v2/view/pages/profile/profile_view/tabs/MediaTab.tsx index 99580a6a32..cf7895551a 100644 --- a/packages/fakeyou.com/src/v2/view/pages/profile/profile_view/tabs/MediaTab.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/profile/profile_view/tabs/MediaTab.tsx @@ -39,7 +39,7 @@ export default function MediaTab({ username }: { username: string }) { listSet, onInputChange: () => setShowMasonryGrid(false), onSuccess: (res) => { - ratings.gather(res); + bookmarks.gather({ res }); setShowMasonryGrid(true); }, requestList: true, diff --git a/packages/fakeyou.com/src/v2/view/pages/profile/profile_view/tabs/WeightsTab.tsx b/packages/fakeyou.com/src/v2/view/pages/profile/profile_view/tabs/WeightsTab.tsx index 83070ca20b..17774386a5 100644 --- a/packages/fakeyou.com/src/v2/view/pages/profile/profile_view/tabs/WeightsTab.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/profile/profile_view/tabs/WeightsTab.tsx @@ -48,7 +48,10 @@ export default function WeightsTab({ username }: { username: string }) { list, listSet, onInputChange: () => setShowMasonryGrid(false), - onSuccess: () => setShowMasonryGrid(true), + onSuccess: (res) => { + bookmarks.gather({ res }); + setShowMasonryGrid(true); + }, requestList: true, urlParam: username, });