diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 2ba295f83..8b4026a20 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -35,7 +35,7 @@ export const parameters = { // { name: 'light', value: '#ffffff' }, // { name: 'dark', value: '#111' }, // { name: 'aqua', value: '#6aadff' }, - // { name: 'kawai', value: '#ffbde6' }, + // { name: 'kawaii', value: '#ffbde6' }, // { name: 'midnight', value: '#002633' }, // { name: 'coffee', value: '#170a06' }, // ], diff --git a/LICENSE b/LICENSE index 5a6c2f781..2f4da8b0b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ MIT License Copyright (c) 2021 Rafael Lima -Copyright (c) 2022 Teia Community +Copyright (c) 2022-2023 Teia Community Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/atoms/logo/index.jsx b/src/atoms/logo/index.tsx similarity index 85% rename from src/atoms/logo/index.jsx rename to src/atoms/logo/index.tsx index 4530ccadb..dfd46f17c 100644 --- a/src/atoms/logo/index.jsx +++ b/src/atoms/logo/index.tsx @@ -5,7 +5,7 @@ import { memo } from 'react' import { useMemo } from 'react' import { RotatingLogoSVG } from '@icons' import { randomSeed } from '@utils' -export const RotatingLogoRemote = ({ className, seed = 1 }) => { +export const RotatingLogoRemote = ({ className = '', seed = 1 }) => { const theme = useLocalSettings((state) => state.theme) const { logos } = useSettings() @@ -30,14 +30,14 @@ export const RotatingLogoRemote = ({ className, seed = 1 }) => { ) } -export const RotatingLogo = ({ className, seed = 1 }) => { +export const RotatingLogo = ({ className = '', seed = 1 }) => { const Logo = useMemo(() => { return RotatingLogoSVG[ Math.floor(randomSeed(seed) * RotatingLogoSVG.length) ] }, [seed]) return ( -
+
) diff --git a/src/atoms/token-collection/index.jsx b/src/atoms/token-collection/index.jsx index cb7b4be13..32c7341ff 100644 --- a/src/atoms/token-collection/index.jsx +++ b/src/atoms/token-collection/index.jsx @@ -68,7 +68,7 @@ function MasonryView({ tokens }) { * @param {number} tkProps.itemsPerLoad - Batch size * @param {number} tkProps.maxItems - Max items to fetch from the indexer * @param {(data:NFT, extra:import("@types").TokenResponse) => [NFT]} tkProps.extractTokensFromResponse - Function to filter the response - * @param {([NFT]) => [NFT]} tkProps.postProcessTokens - Final filter pass over tokens? + * @param {(tokens:NFT[]) => NFT[]} tkProps.postProcessTokens - Final filter pass over tokens? * @returns {React.ReactElement} The feed */ function TokenCollection({ diff --git a/src/components/collab/sign/SigningUI.jsx b/src/components/collab/sign/SigningUI.jsx index 5f9d7bf5e..425e4e172 100644 --- a/src/components/collab/sign/SigningUI.jsx +++ b/src/components/collab/sign/SigningUI.jsx @@ -6,7 +6,7 @@ export const SigningUI = ({ id, hasSigned }) => { const sign = useCollabStore((st) => st.sign) const do_sign = () => { - sign(id).then((response) => console.log(response)) + sign(id).then((response) => console.debug(response)) } return hasSigned ? ( diff --git a/src/components/header/Header.jsx b/src/components/header/Header.tsx similarity index 93% rename from src/components/header/Header.jsx rename to src/components/header/Header.tsx index aafe54768..3cbe419f8 100644 --- a/src/components/header/Header.jsx +++ b/src/components/header/Header.tsx @@ -26,6 +26,7 @@ import { useLocalSettings } from '@context/localSettingsStore' import { useUserStore } from '@context/userStore' import { useModalStore } from '@context/modalStore' import { shallow } from 'zustand/shallow' +import { ArtistProfile } from '@types' export const Header = () => { const [ @@ -77,9 +78,9 @@ export const Header = () => { const isWide = useMedia('(min-width: 600px)') - const [logoSeed, setLogoSeed] = useState() + const [logoSeed, setLogoSeed] = useState() /** the header is a bit larger just on home */ - const [onHome, setOnHome] = useState() + const [onHome, setOnHome] = useState() const [syncLabel, setSyncLabel] = useState('Sync') const [walletLabel, setWalletLabel] = useState('') @@ -97,7 +98,12 @@ export const Header = () => { // on Menu Toggle or Sign in useEffect(() => { - const updateTitle = ([address, proxyAddress, proxyName, userInfo]) => { + const updateTitle = ([address, proxyAddress, proxyName, userInfo]: [ + string | undefined, + string | undefined, + string | undefined, + ArtistProfile + ]) => { setSyncLabel(address ? 'Unsync' : 'Sync') if (address) { // is menu closed? @@ -118,12 +124,18 @@ export const Header = () => { } return useUserStore.subscribe( - (st) => [st.address, st.proxyAddress, st.proxyName, st.userInfo], + (st) => + [st.address, st.proxyAddress, st.proxyName, st.userInfo] as [ + string | undefined, + string | undefined, + string | undefined, + ArtistProfile + ], updateTitle ) }, []) - const handleRoute = (path, data) => { + const handleRoute = (path: string, data?: string) => { setCollapsed(true) navigate(path, { state: data }) } diff --git a/src/components/header/feed_toolbar/FeedToolbar.jsx b/src/components/header/feed_toolbar/FeedToolbar.jsx index 18edc6b7a..d3d264ee5 100644 --- a/src/components/header/feed_toolbar/FeedToolbar.jsx +++ b/src/components/header/feed_toolbar/FeedToolbar.jsx @@ -26,6 +26,7 @@ const locationMap = new Map([ ['/feed/sales', 'Recent Sales'], ['/feed/random', 'Random'], ['/feed/newobjkts', 'New OBJKTs'], + ['/feed/1-1', '1 / 1'], ['/feed/friends', 'Friends'], // separator ['---fund_feeds', 'fund_feeds'], diff --git a/src/constants.ts b/src/constants.ts index 42c9b0deb..5eb0fc9d0 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -205,7 +205,7 @@ export const LICENSE_TYPES_OPTIONS = Object.keys(LICENSE_TYPES).map((k) => ({ export const THEMES: { [key: string]: string } = { dark: 'Dark', light: 'Light', - kawai: 'Kawai', + kawaii: 'Kawaii', aqua: 'Aqua', coffee: 'Coffee', midnight: 'Midnight', diff --git a/src/context/collabStore.ts b/src/context/collabStore.ts index 408a1bca3..584f51f0a 100644 --- a/src/context/collabStore.ts +++ b/src/context/collabStore.ts @@ -40,10 +40,10 @@ export const useCollabStore = create()( .then((response) => { const { data } = response - console.log('response from originations call', data[0]) + console.debug('response from originations call', data[0]) if (data[0]) { - console.log('There is correct data', data[0]) + console.debug('There is correct data', data[0]) // Send the originated contract to the UI via context const { originatedContract } = data[0] @@ -53,7 +53,7 @@ export const useCollabStore = create()( originationOpHash: undefined, }) // save hash - console.log( + console.debug( 'Saved state originatedContract', originatedContract ) @@ -68,7 +68,7 @@ export const useCollabStore = create()( closeModal() }, 2500) } else { - console.log('missing data') + console.error('missing data') // We have got our contract address showModal( @@ -101,7 +101,7 @@ export const useCollabStore = create()( const step = useModalStore.getState().step const handleOp = useUserStore.getState().handleOp - console.log('originateProxy', participantData) + console.debug('originateProxy', participantData) // Clear any existing calls set({ diff --git a/src/context/localSettingsStore.ts b/src/context/localSettingsStore.ts index c4636e81f..95e4a7be5 100644 --- a/src/context/localSettingsStore.ts +++ b/src/context/localSettingsStore.ts @@ -9,7 +9,7 @@ import { FEED_LIST, DEFAULT_START_FEED } from '@constants' type ViewMode = 'single' | 'masonry' -export type Theme = 'dark' | 'light' | 'kawai' | 'aqua' | 'coffee' | 'midnight' +export type Theme = 'dark' | 'light' | 'kawaii' | 'aqua' | 'coffee' | 'midnight' export const rpc_nodes = [ 'https://mainnet.api.tez.ie', diff --git a/src/context/mintStore.ts b/src/context/mintStore.ts index 7b8805229..f3984b0ec 100644 --- a/src/context/mintStore.ts +++ b/src/context/mintStore.ts @@ -306,7 +306,7 @@ export const useMintStore = create()( // optional return (state, error) => { if (error) { - console.log('an error happened during hydration', error) + console.error('an error happened during hydration', error) } } }, diff --git a/src/context/userStore.ts b/src/context/userStore.ts index 25e87d20c..60d970cbb 100644 --- a/src/context/userStore.ts +++ b/src/context/userStore.ts @@ -167,7 +167,7 @@ export const useUserStore = create()( try { step( title, - `Awaiting for confirmation of the [operation](https://tzkt.io/${op.opHash}) + `Awaiting confirmation of the [operation](https://tzkt.io/${op.opHash}) *closing this dialog has no effect on the transaction*` ) // skippable @@ -208,18 +208,15 @@ export const useUserStore = create()( const current = await wallet.getPKH() if (current) { const info = await getUser(current) - console.log('getting user info', info) set({ address: current, - userInfo: await getUser(current), + userInfo: info, }) } - // console.log(this.state) return current }, unsync: async () => { - // console.log('disconnect wallet') // This will clear the active account and the next "syncTaquito" will trigger a new sync await wallet.client.clearActiveAccount() set({ diff --git a/src/index.jsx b/src/index.jsx index f0cc5d5f3..d8b26d64b 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -17,19 +17,20 @@ import { IranFeed, PakistanFeed, UkraineFeed, + QuakeFeed, AudioFeed, GifFeed, GlbFeed, HtmlSvgFeed, ImageFeed, VideoFeed, + OneOnOneFeed, NewObjktsFeed, RandomFeed, RecentSalesFeed, TagFeed, PdfFeed, MarkdownFeed, - QuakeFeed, MoroccoQuakeFeed, } from '@pages/home/feeds' import Mint from '@pages/mint' @@ -80,7 +81,7 @@ const router = createBrowserRouter( }> } /> } /> - + } /> } diff --git a/src/pages/home/feeds/index.js b/src/pages/home/feeds/index.js index 6cbd94b11..841507808 100644 --- a/src/pages/home/feeds/index.js +++ b/src/pages/home/feeds/index.js @@ -1,5 +1,5 @@ export { NewObjktsFeed } from './new-objkts-feed' - +export { OneOnOneFeed } from './one-one-feed' export { AudioFeed, GifFeed, diff --git a/src/pages/home/feeds/one-one-feed.jsx b/src/pages/home/feeds/one-one-feed.jsx new file mode 100644 index 000000000..c03d75c93 --- /dev/null +++ b/src/pages/home/feeds/one-one-feed.jsx @@ -0,0 +1,32 @@ +import { gql } from 'graphql-request' +import uniqBy from 'lodash/uniqBy' +import { BaseTokenFieldsFragment } from '@data/api' +import { HEN_CONTRACT_FA2 } from '@constants' +import TokenCollection from '@atoms/token-collection' + +export function OneOnOneFeed() { + return ( + uniqBy(tokens, 'artist_address')} + query={gql` + ${BaseTokenFieldsFragment} + query getUnoUno($limit: Int!) { + tokens( + where: { metadata_status: { _eq: "processed" }, fa2_address: { _eq: "${HEN_CONTRACT_FA2}"}, editions: {_eq: 1} } + order_by: { minted_at: desc } + limit: $limit + + ) { + ...baseTokenFields + } + } + `} + /> + ) +} + +export default OneOnOneFeed diff --git a/src/pages/objkt-display/tabs/Swap.jsx b/src/pages/objkt-display/tabs/Swap.jsx index f4b5824e6..3c9f5b4a2 100644 --- a/src/pages/objkt-display/tabs/Swap.jsx +++ b/src/pages/objkt-display/tabs/Swap.jsx @@ -75,15 +75,7 @@ export const Swap = () => { } // swap is valid call API - console.debug( - address, - nft.royalties_total, - parseFloat(price) * 1000000, - id, - nft.artist_address, - parseFloat(amount) - ) - console.log([ + console.debug([ address, nft.royalties_total, parseFloat(price) * 1e6, diff --git a/src/pages/profile/collabs.jsx b/src/pages/profile/collabs.jsx index 39c8cde4a..0eed275d5 100644 --- a/src/pages/profile/collabs.jsx +++ b/src/pages/profile/collabs.jsx @@ -9,7 +9,7 @@ import Checkbox from '@atoms/input/Checkbox' import styles from '@style' export default function Collabs() { - const { /*showFilters,*/ showRestricted, address, overrideProtections } = + const { /*showFilters,*/ showRestricted, user_address, overrideProtections } = useOutletContext() const [hasUnverifiedTokens, setHasUnverifiedTokens] = useState(false) @@ -33,8 +33,8 @@ export default function Collabs() { overrideProtections={overrideProtections} label="Artist's Collabs" namespace="collabs" - swrParams={[address]} - variables={{ address }} + swrParams={[user_address]} + variables={{ address: user_address }} emptyMessage="no collabs" maxItems={null} extractTokensFromResponse={(data) => { diff --git a/src/pages/profile/collections.jsx b/src/pages/profile/collections.jsx index 06e1e18c4..313625c95 100644 --- a/src/pages/profile/collections.jsx +++ b/src/pages/profile/collections.jsx @@ -12,7 +12,7 @@ const FILTER_FOR_SALE = 'FOR_SALE' const FILTER_NOT_FOR_SALE = 'NOT_FOR_SALE' export default function Collections() { - const { showFilters, showRestricted, overrideProtections, address } = + const { showFilters, showRestricted, overrideProtections, user_address } = useOutletContext() const [filter, setFilter] = useState(FILTER_ALL) @@ -36,25 +36,28 @@ export default function Collections() { overrideProtections={overrideProtections} label="Artist's Collection" namespace="collections" - swrParams={[address]} - variables={{ address }} + swrParams={[user_address]} + variables={{ address: user_address }} emptyMessage="no collections" maxItems={null} postProcessTokens={(tokens) => { - if (filter === FILTER_FOR_SALE) { - return tokens.filter( - ({ listing_seller_address }) => listing_seller_address === address - ) - } + switch (filter) { + case FILTER_FOR_SALE: + return tokens.filter( + ({ listing_seller_address }) => + listing_seller_address === user_address + ) - if (filter === FILTER_NOT_FOR_SALE) { - return tokens.filter( - ({ listing_seller_address, artist_address }) => - artist_address !== address && listing_seller_address !== address - ) - } + case FILTER_NOT_FOR_SALE: + return tokens.filter( + ({ listing_seller_address, artist_address }) => + artist_address !== user_address && + listing_seller_address !== user_address + ) - return tokens + default: + return tokens + } }} extractTokensFromResponse={(data, { postProcessTokens }) => { const heldTokens = data.holdings.map(({ token }) => token) diff --git a/src/pages/profile/creations.jsx b/src/pages/profile/creations.jsx index 533bd6cbb..d96b81308 100644 --- a/src/pages/profile/creations.jsx +++ b/src/pages/profile/creations.jsx @@ -7,18 +7,20 @@ import TokenCollection from '@atoms/token-collection' import Filters from './filters' import { useOutletContext } from 'react-router' -import { orderBy } from 'lodash' +import { flatMap, orderBy } from 'lodash' +import { useUserStore } from '@context/userStore' const FILTER_ALL = 'ALL' const FILTER_PRIMARY = 'PRIMARY' const FILTER_SECONDARY = 'SECONDARY' const FILTER_NOT_FOR_SALE = 'NOT_FOR_SALE' +const FILTER_OWNED = 'OWNED' export default function Creations() { - const { showFilters, showRestricted, overrideProtections, address } = + const { showFilters, showRestricted, overrideProtections, user_address } = useOutletContext() + const address = useUserStore((st) => st.address) const [filter, setFilter] = useState(FILTER_ALL) - return ( <> {showFilters && ( @@ -30,6 +32,7 @@ export default function Creations() { { type: FILTER_PRIMARY, label: 'Primary' }, { type: FILTER_SECONDARY, label: 'Secondary' }, { type: FILTER_NOT_FOR_SALE, label: 'Not for sale' }, + { type: FILTER_OWNED, label: 'Owned' }, ]} /> )} @@ -39,11 +42,11 @@ export default function Creations() { overrideProtections={overrideProtections} label="Artist's Creations" namespace="creations" - swrParams={[address]} - variables={{ address }} + swrParams={[user_address]} + variables={{ address: user_address }} emptyMessage="no creations" maxItems={null} - extractTokensFromResponse={(data) => { + extractTokensFromResponse={(data, { postProcessTokens }) => { const tokens = data.artist_tokens const collab_tokens = data.artist_single_collabs .map((collab) => { @@ -56,33 +59,43 @@ export default function Creations() { }) .flat() - return orderBy([...tokens, ...collab_tokens], ['minted_at']) - .reverse() - .map((token) => ({ ...token, key: token.token_id })) + return postProcessTokens( + orderBy([...tokens, ...collab_tokens], ['minted_at']) + .reverse() + .map((token) => ({ ...token, key: token.token_id })) + ) }} postProcessTokens={(tokens) => { - if (filter === FILTER_PRIMARY) { - return tokens.filter( - (token) => - get(token, 'lowest_price_listing.seller_address') === address - ) - } - - if (filter === FILTER_SECONDARY) { - return tokens.filter( - (token) => - get(token, 'lowest_price_listing.seller_address') !== address - ) - } + switch (filter) { + case FILTER_PRIMARY: + return tokens.filter( + (token) => + get(token, 'lowest_price_listing.seller_address') === + user_address + ) + case FILTER_SECONDARY: + return tokens.filter( + (token) => + get(token, 'lowest_price_listing.seller_address') !== + user_address + ) + case FILTER_NOT_FOR_SALE: + return tokens.filter( + (token) => get(token, 'lowest_price_listing') === null + ) - if (filter === FILTER_NOT_FOR_SALE) { - return tokens.filter( - (token) => get(token, 'lowest_price_listing') === null - ) + case FILTER_OWNED: + return tokens.filter((t) => { + const holders = flatMap(t.holdings, 'holder_address') + if (holders.includes(address)) { + return true + } + return false + }) + default: + // all tokens + return tokens } - - // all tokens - return tokens }} query={gql` ${BaseTokenFieldsFragment} @@ -98,6 +111,9 @@ export default function Creations() { ) { ...baseTokenFields lowest_price_listing + holdings(where: {amount: {_gt: 0}}) { + holder_address + } } artist_single_collabs: teia_shareholders( where: { diff --git a/src/pages/profile/filters.jsx b/src/pages/profile/filters.jsx index c2d2e0ee4..1e5964143 100644 --- a/src/pages/profile/filters.jsx +++ b/src/pages/profile/filters.jsx @@ -1,16 +1,16 @@ import { Button } from '@atoms/button' -import { Container } from '@atoms/layout' import styles from '@style' function FilterButton({ type, children, isActive, onClick }) { return (