diff --git a/biome.json b/biome.json index fb053579..c9d7601d 100644 --- a/biome.json +++ b/biome.json @@ -27,6 +27,16 @@ "indentWidth": 2, "indentStyle": "space", "formatWithErrors": true, + "include": [ + "./**/*.ts", + "./**/*.js", + "./**/*.cjs", + "./**/*.tsx", + "./**/*.d.ts", + "./**/*.json", + "./**/*.jsonc", + "!next-env.d.ts" + ], "ignore": ["node_modules", "dist", "package.json", "tsconfig.json", "_"] }, "linter": { @@ -40,9 +50,11 @@ "./**/*.jsonc" ], "ignore": ["node_modules", "dist", "package.json", "tsconfig.json", "_"], - "enabled": false, + "enabled": true, "rules": { + "all": true, "style": { + "all": true, "noNamespace": "off", "useImportType": "off", "noDefaultExport": "off", @@ -59,6 +71,7 @@ "noUnusedTemplateLiteral": "off" }, "a11y": { + "all": true, "noAutofocus": "off", "useButtonType": "off", "useMediaCaption": "off", @@ -68,6 +81,7 @@ "noDistractingElements": "off" }, "nursery": { + "all": true, "noEnum": "off", "noSecrets": "off", "noProcessEnv": "off", @@ -82,11 +96,13 @@ "useComponentExportOnlyModules": "off" }, "performance": { + "all": true, "noReExportAll": "off", "useTopLevelRegex": "off", "noAccumulatingSpread": "off" }, "correctness": { + "all": true, "noNodejsModules": "off", "noUnusedVariables": "off", "useImportExtensions": "off", @@ -95,12 +111,14 @@ "useExhaustiveDependencies": "off" }, "suspicious": { + "all": true, "noConsoleLog": "off", "noExplicitAny": "off", "noArrayIndexKey": "off", "noEmptyInterface": "off" }, "complexity": { + "all": true, "noForEach": "off", "noBannedTypes": "off", "useLiteralKeys": "off", diff --git a/bun.lockb b/bun.lockb index 8315fa5f..d3776a90 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 898d86b6..d762b914 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "wagmi": "^2.12.16" }, "devDependencies": { - "@biomejs/biome": "^1.9.4", + "@biomejs/biome": "^1.7.3", "@next/bundle-analyzer": "^14.1.0", "@tailwindcss/aspect-ratio": "^0.4.2", "@tailwindcss/container-queries": "^0.1.1", diff --git a/src/app/[user]/hooks/use-user-profile.ts b/src/app/[user]/hooks/use-user-profile.ts index f11a3c0c..6fe9bda1 100644 --- a/src/app/[user]/hooks/use-user-profile.ts +++ b/src/app/[user]/hooks/use-user-profile.ts @@ -1,106 +1,106 @@ -import { useState } from 'react' -import { isAddress } from 'viem' -import { useInfiniteQuery, useQuery } from '@tanstack/react-query' -import { fetchProfileDetails, fetchProfileStats } from 'ethereum-identity-kit' - -import { FETCH_LIMIT_PARAM } from '#/lib/constants' -import type { ProfileTableTitleType } from '#/types/common' -import { fetchProfileFollowers } from '#/api/followers/fetch-profile-followers' -import { fetchProfileFollowing } from '#/api/following/fetch-profile-following' -import { fetchFollowerTags, nullFollowerTags } from '#/api/followers/fetch-follower-tags' -import type { FollowerResponse, FollowingResponse, FollowSortType } from '#/types/requests' -import { fetchFollowingTags, nullFollowingTags } from '#/api/following/fetch-following-tags' +import { useState } from "react"; +import { isAddress } from "viem"; +import { useInfiniteQuery, useQuery } from "@tanstack/react-query"; +import { fetchProfileDetails, fetchProfileStats } from "ethereum-identity-kit"; + +import { FETCH_LIMIT_PARAM } from "#/lib/constants"; +import type { ProfileTableTitleType } from "#/types/common"; +import { fetchProfileFollowers } from "#/api/followers/fetch-profile-followers"; +import { fetchProfileFollowing } from "#/api/following/fetch-profile-following"; +import { fetchFollowerTags, nullFollowerTags } from "#/api/followers/fetch-follower-tags"; +import type { FollowerResponse, FollowingResponse, FollowSortType } from "#/types/requests"; +import { fetchFollowingTags, nullFollowingTags } from "#/api/following/fetch-following-tags"; const useUser = (user: string) => { - const [fetchFreshProfile, setFetchFreshProfile] = useState(false) - const [followingSearch, setFollowingSearch] = useState('') - const [followersSearch, setFollowersSearch] = useState('') - const [followingTagsFilter, setFollowingTagsFilter] = useState([]) - const [followersTagsFilter, setFollowersTagsFilter] = useState([]) - const [followingSort, setFollowingSort] = useState('follower count') - const [followersSort, setFollowersSort] = useState('follower count') + const [fetchFreshProfile, setFetchFreshProfile] = useState(false); + const [followingSearch, setFollowingSearch] = useState(""); + const [followersSearch, setFollowersSearch] = useState(""); + const [followingTagsFilter, setFollowingTagsFilter] = useState([]); + const [followersTagsFilter, setFollowersTagsFilter] = useState([]); + const [followingSort, setFollowingSort] = useState("follower count"); + const [followersSort, setFollowersSort] = useState("follower count"); - const userIsList = !(isAddress(user) || user.includes('.') || Number.isNaN(Number(user))) - const listNum = userIsList ? Number(user) : undefined + const userIsList = !(isAddress(user) || user.includes(".") || Number.isNaN(Number(user))); + const listNum = userIsList ? Number(user) : undefined; const isValidUser = isAddress(user) || (userIsList && listNum && listNum > 0 && listNum < 1000000000) || - user.includes('.') + user.includes("."); const { data: profile, isLoading: profileIsLoading, isRefetching: isRefetchingProfile, - refetch: refetchProfile + refetch: refetchProfile, } = useQuery({ - queryKey: ['profile', user, fetchFreshProfile], + queryKey: ["profile", user, fetchFreshProfile], queryFn: async () => { - if (!isValidUser) return null + if (!isValidUser) return null; - const fetchedProfile = await fetchProfileDetails(user, listNum, fetchFreshProfile) - return fetchedProfile + const fetchedProfile = await fetchProfileDetails(user, listNum, fetchFreshProfile); + return fetchedProfile; }, staleTime: 30000, - refetchOnWindowFocus: false - }) + refetchOnWindowFocus: false, + }); const { data: stats, isLoading: statsIsLoading, - isRefetching: isRefetchingStatsQuery + isRefetching: isRefetchingStatsQuery, } = useQuery({ - queryKey: ['stats', user], + queryKey: ["stats", user], queryFn: async () => { - if (!isValidUser) return null + if (!isValidUser) return null; - const fetchedStats = await fetchProfileStats(user, listNum) + const fetchedStats = await fetchProfileStats(user, listNum); - return fetchedStats + return fetchedStats; }, // refetchInterval: 60000 - refetchOnWindowFocus: false - }) + refetchOnWindowFocus: false, + }); const { data: followerTags, isLoading: followerTagsLoading, - isRefetching: isRefetchingFollowerTags + isRefetching: isRefetchingFollowerTags, } = useQuery({ - queryKey: ['follower tags', user], + queryKey: ["follower tags", user], queryFn: async () => { - if (!isValidUser) return nullFollowerTags + if (!isValidUser) return nullFollowerTags; - const fetchedTags = await fetchFollowerTags(user, userIsList ? listNum : undefined) - return fetchedTags + const fetchedTags = await fetchFollowerTags(user, userIsList ? listNum : undefined); + return fetchedTags; }, staleTime: 30000, - refetchOnWindowFocus: false - }) + refetchOnWindowFocus: false, + }); - const [isEndOfFollowers, setIsEndOfFollowers] = useState(false) + const [isEndOfFollowers, setIsEndOfFollowers] = useState(false); const { data: fetchedFollowers, isLoading: followersIsLoading, fetchNextPage: fetchMoreFollowers, isFetchingNextPage: isFetchingMoreFollowers, - isRefetching: isRefetchingFollowers + isRefetching: isRefetchingFollowers, } = useInfiniteQuery({ queryKey: [ - 'followers', + "followers", user, followersSort, followersTagsFilter, - followersSearch.length > 2 ? followersSearch : undefined + followersSearch.length > 2 ? followersSearch : undefined, ], queryFn: async ({ pageParam = 0 }) => { - setIsEndOfFollowers(false) + setIsEndOfFollowers(false); if (!isValidUser) return { followers: [], - nextPageParam: pageParam - } + nextPageParam: pageParam, + }; const fetchedFollowers = await fetchProfileFollowers({ addressOrName: user, @@ -109,58 +109,58 @@ const useUser = (user: string) => { pageParam, tags: followersTagsFilter, sort: followersSort, - search: followersSearch - }) + search: followersSearch, + }); - if (fetchedFollowers.followers.length === 0) setIsEndOfFollowers(true) + if (fetchedFollowers.followers.length === 0) setIsEndOfFollowers(true); - return fetchedFollowers + return fetchedFollowers; }, staleTime: 30000, initialPageParam: 0, - getNextPageParam: lastPage => lastPage.nextPageParam, - refetchOnWindowFocus: false - }) + getNextPageParam: (lastPage) => lastPage.nextPageParam, + refetchOnWindowFocus: false, + }); const { data: followingTags, isLoading: followingTagsLoading, - isRefetching: isRefetchingFollowingTags + isRefetching: isRefetchingFollowingTags, } = useQuery({ - queryKey: ['following tags', user], + queryKey: ["following tags", user], queryFn: async () => { - if (!isValidUser) return nullFollowingTags + if (!isValidUser) return nullFollowingTags; - const fetchedTags = await fetchFollowingTags(user, listNum) - return fetchedTags + const fetchedTags = await fetchFollowingTags(user, listNum); + return fetchedTags; }, staleTime: 30000, - refetchOnWindowFocus: false - }) + refetchOnWindowFocus: false, + }); - const [isEndOfFollowing, setIsEndOfFollowing] = useState(false) + const [isEndOfFollowing, setIsEndOfFollowing] = useState(false); const { data: fetchedFollowing, isLoading: followingIsLoading, fetchNextPage: fetchMoreFollowing, isFetchingNextPage: isFetchingMoreFollowing, - isRefetching: isRefetchingFollowing + isRefetching: isRefetchingFollowing, } = useInfiniteQuery({ queryKey: [ - 'following', + "following", user, followingSort, followingTagsFilter, - followingSearch.length > 2 ? followingSearch : undefined + followingSearch.length > 2 ? followingSearch : undefined, ], queryFn: async ({ pageParam = 0 }) => { - setIsEndOfFollowing(false) + setIsEndOfFollowing(false); if (!isValidUser) return { following: [], - nextPageParam: pageParam - } + nextPageParam: pageParam, + }; const fetchedFollowing = await fetchProfileFollowing({ addressOrName: user, @@ -169,50 +169,50 @@ const useUser = (user: string) => { pageParam, tags: followingTagsFilter, sort: followingSort, - search: followingSearch - }) + search: followingSearch, + }); - if (fetchedFollowing.following.length === 0) setIsEndOfFollowing(true) + if (fetchedFollowing.following.length === 0) setIsEndOfFollowing(true); - return fetchedFollowing + return fetchedFollowing; }, staleTime: 30000, initialPageParam: 0, - getNextPageParam: lastPage => lastPage.nextPageParam, - refetchOnWindowFocus: false - }) + getNextPageParam: (lastPage) => lastPage.nextPageParam, + refetchOnWindowFocus: false, + }); const followers = fetchedFollowers ? fetchedFollowers.pages.reduce( (acc, el) => [...acc, ...el.followers], [] as FollowerResponse[] ) - : [] + : []; const following = fetchedFollowing ? fetchedFollowing.pages.reduce( (acc, el) => [...acc, ...el.following], [] as FollowingResponse[] ) - : [] + : []; const toggleTag = (tab: ProfileTableTitleType, tag: string) => { - if (tab === 'following') { + if (tab === "following") { if (followingTagsFilter.includes(tag)) { - setFollowingTagsFilter(followingTagsFilter.filter(item => item !== tag)) + setFollowingTagsFilter(followingTagsFilter.filter((item) => item !== tag)); } else { - setFollowingTagsFilter([...followingTagsFilter, tag]) + setFollowingTagsFilter([...followingTagsFilter, tag]); } } - if (tab === 'followers') { + if (tab === "followers") { if (followersTagsFilter.includes(tag)) { - setFollowersTagsFilter(followersTagsFilter.filter(item => item !== tag)) + setFollowersTagsFilter(followersTagsFilter.filter((item) => item !== tag)); } else { - setFollowersTagsFilter([...followersTagsFilter, tag]) + setFollowersTagsFilter([...followersTagsFilter, tag]); } } - } + }; return { stats, @@ -248,8 +248,8 @@ const useUser = (user: string) => { setFollowingTagsFilter, refetchProfile, fetchFreshProfile, - setFetchFreshProfile - } -} + setFetchFreshProfile, + }; +}; -export default useUser +export default useUser; diff --git a/src/app/leaderboard/components/table.tsx b/src/app/leaderboard/components/table.tsx index 6a652a94..ab2e1567 100644 --- a/src/app/leaderboard/components/table.tsx +++ b/src/app/leaderboard/components/table.tsx @@ -141,21 +141,23 @@ const LeaderboardTable = () => {
- {leaderboard?.slice(0, chunk * LEADERBOARD_CHUNK_SIZE).map((entry: LeaderboardItem) => ( - - ))} + {leaderboard + ?.slice(0, chunk * LEADERBOARD_CHUNK_SIZE) + .map((entry: LeaderboardItem) => ( + + ))} {new Array(isLoading ? LEADERBOARD_CHUNK_SIZE : 0).fill(1).map((_, i) => ( ))} diff --git a/src/app/swipe/components/useRecommendedProfilesCards.ts b/src/app/swipe/components/useRecommendedProfilesCards.ts index afc9ac18..7e81ac5c 100644 --- a/src/app/swipe/components/useRecommendedProfilesCards.ts +++ b/src/app/swipe/components/useRecommendedProfilesCards.ts @@ -55,45 +55,47 @@ export const useRecommendedProfilesCards = () => { [fetchNextPage, recommendedProfiles] ) - const bindDragToCards = useDrag(({ args: [index], down, movement: [mx], direction: [xDir] }) => { - if (index !== gone.size) return - const trigger = (mx > 50 && xDir === 1) || (mx < -50 && xDir === -1) // Card has to be moved more than 50px in either direction to trigger the swipe + const bindDragToCards = useDrag( + ({ args: [index], down, movement: [mx], direction: [xDir] }) => { + if (index !== gone.size) return + const trigger = (mx > 50 && xDir === 1) || (mx < -50 && xDir === -1) // Card has to be moved more than 50px in either direction to trigger the swipe + + if (!down && trigger) { + setDidSwipeBack(false) + gone.add(index) + + if (canFetchMoreProfiles(index)) fetchNextPage() + if (xDir === 1) { + setTimeout(() => { + addCartItem({ + listOp: listOpAddListRecord( + // @ts-ignore the index comes from the cardsApi which is the same length as recommendedProfiles + recommendedProfiles[index].address + ) + }) + handleStartAnimationAndSound() + }, 0.15 * SECOND) + } + } - if (!down && trigger) { - setDidSwipeBack(false) - gone.add(index) + cardsApi.start(i => { + if (index !== i) return - if (canFetchMoreProfiles(index)) fetchNextPage() - if (xDir === 1) { - setTimeout(() => { - addCartItem({ - listOp: listOpAddListRecord( - // @ts-ignore the index comes from the cardsApi which is the same length as recommendedProfiles - recommendedProfiles[index].address - ) - }) - handleStartAnimationAndSound() - }, 0.15 * SECOND) - } - } + const isGone = gone.has(index) + const x = isGone ? (250 + window.innerWidth / 1.5) * xDir : down ? mx : 0 // When a card is gone it flys out left or right, otherwise goes back to zero + const rot = mx / 100 + (isGone ? xDir * 10 : 0) // How much the card tilts + const scale = down ? 1.075 : 1 // Active cards lift up a bit - cardsApi.start(i => { - if (index !== i) return - - const isGone = gone.has(index) - const x = isGone ? (250 + window.innerWidth / 1.5) * xDir : down ? mx : 0 // When a card is gone it flys out left or right, otherwise goes back to zero - const rot = mx / 100 + (isGone ? xDir * 10 : 0) // How much the card tilts - const scale = down ? 1.075 : 1 // Active cards lift up a bit - - return { - x, - rot, - scale, - delay: undefined, - config: { friction: 80, tension: down ? 800 : isGone ? 250 : 800 } - } - }) - }) + return { + x, + rot, + scale, + delay: undefined, + config: { friction: 80, tension: down ? 800 : isGone ? 250 : 800 } + } + }) + } + ) const onSwipeLeft = useCallback(() => { if (recommendedProfiles.length === 0 || isLoading || gone.size === recommendedProfiles.length) diff --git a/src/app/team/hooks/use-members.ts b/src/app/team/hooks/use-members.ts index aac98c6b..f3732497 100644 --- a/src/app/team/hooks/use-members.ts +++ b/src/app/team/hooks/use-members.ts @@ -1,55 +1,55 @@ -import { useQuery } from '@tanstack/react-query' -import { fetchProfileDetails, fetchProfileStats } from 'ethereum-identity-kit' -import { TEAM_ADDRESSES, FOUNDATION_ADDRESSES } from '#/lib/constants/team' +import { useQuery } from "@tanstack/react-query"; +import { fetchProfileDetails, fetchProfileStats } from "ethereum-identity-kit"; +import { TEAM_ADDRESSES, FOUNDATION_ADDRESSES } from "#/lib/constants/team"; export const useMembers = () => { const { data: teamProfiles, isLoading: teamIsLoading } = useQuery({ - queryKey: ['team', TEAM_ADDRESSES], + queryKey: ["team", TEAM_ADDRESSES], queryFn: async () => { - if (!TEAM_ADDRESSES) return [] + if (!TEAM_ADDRESSES) return []; const data = await Promise.all( - TEAM_ADDRESSES.map(async address => await fetchProfileDetails(address)) - ) - return data - } - }) + TEAM_ADDRESSES.map(async (address) => await fetchProfileDetails(address)) + ); + return data; + }, + }); const { data: teamStats, isLoading: teamStatsIsLoading } = useQuery({ - queryKey: ['team', 'stats', TEAM_ADDRESSES], + queryKey: ["team", "stats", TEAM_ADDRESSES], queryFn: async () => { - if (!TEAM_ADDRESSES) return [] + if (!TEAM_ADDRESSES) return []; const data = await Promise.all( - TEAM_ADDRESSES.map(async address => await fetchProfileStats(address)) - ) + TEAM_ADDRESSES.map(async (address) => await fetchProfileStats(address)) + ); - return data - } - }) + return data; + }, + }); const { data: foundationProfiles, isLoading: foundationIsLoading } = useQuery({ - queryKey: ['follow protocol foundation', FOUNDATION_ADDRESSES], + queryKey: ["follow protocol foundation", FOUNDATION_ADDRESSES], queryFn: async () => { - if (!FOUNDATION_ADDRESSES) return [] + if (!FOUNDATION_ADDRESSES) return []; const data = await Promise.all( - FOUNDATION_ADDRESSES.map(async address => await fetchProfileDetails(address)) - ) - return data - } - }) + FOUNDATION_ADDRESSES.map(async (address) => await fetchProfileDetails(address)) + ); + return data; + }, + }); const { data: foundationStats, isLoading: foundationStatsIsLoading } = useQuery({ - queryKey: ['follow protocol foundation', 'stats', FOUNDATION_ADDRESSES], + queryKey: ["follow protocol foundation", "stats", FOUNDATION_ADDRESSES], queryFn: async () => { - if (!FOUNDATION_ADDRESSES) return [] + if (!FOUNDATION_ADDRESSES) return []; const data = await Promise.all( - FOUNDATION_ADDRESSES.map(async address => await fetchProfileStats(address)) - ) + FOUNDATION_ADDRESSES.map(async (address) => await fetchProfileStats(address)) + ); - return data - } - }) + return data; + }, + }); return { teamProfiles, @@ -59,6 +59,6 @@ export const useMembers = () => { teamIsLoading, teamStatsIsLoading, foundationIsLoading, - foundationStatsIsLoading - } -} + foundationStatsIsLoading, + }; +}; diff --git a/src/components/footer/components/pages.tsx b/src/components/footer/components/pages.tsx index 3272ce3c..fb840550 100644 --- a/src/components/footer/components/pages.tsx +++ b/src/components/footer/components/pages.tsx @@ -35,7 +35,7 @@ const Pages = () => { return (
- {footerPages.map(route => ( + {footerPages.map((route) => (
{ pathname?.toLowerCase() === `/${userAddress?.toLowerCase()}` && selectedList === Number(lists?.primary_list) ? userAddress?.toLowerCase() - : (selectedList?.toString() ?? userAddress?.toLowerCase()) + : selectedList?.toString() ?? userAddress?.toLowerCase() const itemIndex = EXTERNAL_LINKS.find(link => link.href === pathname) ? 4 : NAV_ITEMS.findIndex(item => item.href(itemUrl) === pathname) diff --git a/src/components/search/hooks/useSearch.ts b/src/components/search/hooks/useSearch.ts index b5e3ae3b..a082508d 100644 --- a/src/components/search/hooks/useSearch.ts +++ b/src/components/search/hooks/useSearch.ts @@ -1,43 +1,43 @@ -import { toast } from 'sonner' -import { useAccount } from 'wagmi' -import { useTranslation } from 'react-i18next' -import { isAddress, type Address } from 'viem' -import { useQuery } from '@tanstack/react-query' -import { useClickAway } from '@uidotdev/usehooks' -import { usePathname, useRouter } from 'next/navigation' -import { useEffect, useMemo, useRef, useState } from 'react' -import { fetchFollowState } from 'ethereum-identity-kit' - -import { SECOND } from '#/lib/constants' -import { resolveEnsAddress } from '#/utils/ens' -import { useCart } from '#/contexts/cart-context.tsx' -import { searchENSNames } from '#/api/search-ens-names' -import { listOpAddListRecord } from '#/utils/list-ops.ts' -import { formatError } from '#/utils/format/format-error' -import { useEFPProfile } from '#/contexts/efp-profile-context.tsx' +import { toast } from "sonner"; +import { useAccount } from "wagmi"; +import { useTranslation } from "react-i18next"; +import { isAddress, type Address } from "viem"; +import { useQuery } from "@tanstack/react-query"; +import { useClickAway } from "@uidotdev/usehooks"; +import { usePathname, useRouter } from "next/navigation"; +import { useEffect, useMemo, useRef, useState } from "react"; +import { fetchFollowState } from "ethereum-identity-kit"; + +import { SECOND } from "#/lib/constants"; +import { resolveEnsAddress } from "#/utils/ens"; +import { useCart } from "#/contexts/cart-context.tsx"; +import { searchENSNames } from "#/api/search-ens-names"; +import { listOpAddListRecord } from "#/utils/list-ops.ts"; +import { formatError } from "#/utils/format/format-error"; +import { useEFPProfile } from "#/contexts/efp-profile-context.tsx"; const useSearch = (isEditor?: boolean) => { - const [isAddingToCart, setIsAddingToCart] = useState(false) - const [dropdownMenuOpen, setDropdownMenuOpen] = useState(false) - const [dialogOpen, setDialogOpen] = useState(undefined) + const [isAddingToCart, setIsAddingToCart] = useState(false); + const [dropdownMenuOpen, setDropdownMenuOpen] = useState(false); + const [dialogOpen, setDialogOpen] = useState(undefined); // const searchParams = useSearchParams() // const initialSearch = searchParams.get('search') - const [currentSearch, setCurrentSearch] = useState('') - const [search, setSearch] = useState('') - - const router = useRouter() - const pathname = usePathname() - const { t } = useTranslation() - const { address: userAddress } = useAccount() - const { roles, selectedList } = useEFPProfile() - const { addCartItem, hasListOpAddRecord, setLoadingCartItems } = useCart() - - const clickAwayRef = useClickAway(_ => { - setDropdownMenuOpen(false) - setDialogOpen(false) - }) - const searchBarRef = useRef(null) + const [currentSearch, setCurrentSearch] = useState(""); + const [search, setSearch] = useState(""); + + const router = useRouter(); + const pathname = usePathname(); + const { t } = useTranslation(); + const { address: userAddress } = useAccount(); + const { roles, selectedList } = useEFPProfile(); + const { addCartItem, hasListOpAddRecord, setLoadingCartItems } = useCart(); + + const clickAwayRef = useClickAway((_) => { + setDropdownMenuOpen(false); + setDialogOpen(false); + }); + const searchBarRef = useRef(null); // useEffect(() => { // if (initialSearch && initialSearch?.length > 0 && searchBarRef) { @@ -49,202 +49,206 @@ const useSearch = (isEditor?: boolean) => { // }, [searchBarRef]) useEffect(() => { - if (dialogOpen) searchBarRef.current?.focus() - }, [dialogOpen]) + if (dialogOpen) searchBarRef.current?.focus(); + }, [dialogOpen]); const searchKey = useMemo( () => (isEditor ? currentSearch : search), [isEditor, search, currentSearch] - ) + ); const { data, status: searchResultStatus, - isLoading + isLoading, } = useQuery({ - queryKey: ['ens-subgraph-search', { seaarch: searchKey }], - queryFn: async () => await searchENSNames({ search: searchKey ?? '' }), + queryKey: ["ens-subgraph-search", { seaarch: searchKey }], + queryFn: async () => await searchENSNames({ search: searchKey ?? "" }), refetchOnMount: false, refetchOnReconnect: false, refetchOnWindowFocus: false, refetchIntervalInBackground: false, - enabled: Boolean(searchKey && searchKey.length > 0) - }) + enabled: Boolean(searchKey && searchKey.length > 0), + }); const searchResult = - searchResultStatus !== 'pending' + searchResultStatus !== "pending" ? !data || data.length === 0 ? !(isEditor || isAddress(searchKey)) && (!Number.isNaN(Number(searchKey)) || - (searchKey[0] === '#' && !Number.isNaN(Number(searchKey.slice(1))))) + (searchKey[0] === "#" && !Number.isNaN(Number(searchKey.slice(1))))) + ? [ + { + name: `#${searchKey[0] === "#" ? searchKey.slice(1) : searchKey}`, + resolvedAddress: null, + }, + ] + : !isEditor && searchKey.includes(".") + ? [ + { + name: searchKey, + resolvedAddress: null, + }, + ] + : isAddress(searchKey) ? [ { - name: `#${searchKey[0] === '#' ? searchKey.slice(1) : searchKey}`, - resolvedAddress: null - } + name: searchKey, + resolvedAddress: { id: searchKey }, + }, ] - : !isEditor && searchKey.includes('.') - ? [ - { - name: searchKey, - resolvedAddress: null - } - ] - : isAddress(searchKey) - ? [ - { - name: searchKey, - resolvedAddress: { id: searchKey } - } - ] - : [] + : [] : data.slice(0, 5) - : [] + : []; const resetSearch = () => { - setCurrentSearch('') - setDialogOpen(false) - setDropdownMenuOpen(false) - searchBarRef.current?.blur() - } + setCurrentSearch(""); + setDialogOpen(false); + setDropdownMenuOpen(false); + searchBarRef.current?.blur(); + }; const getFollowingState = async (address: Address) => { - if (!userAddress) return 'none' + if (!userAddress) return "none"; const followingStatus = await fetchFollowState({ lookupAddressOrName: address, connectedAddress: userAddress, list: selectedList, - type: 'following' - }) + type: "following", + }); - if (!followingStatus) return 'none' - if (followingStatus.state.block) return 'blocks' - if (followingStatus.state.mute) return 'mutes' - if (followingStatus.state.follow) return 'follows' + if (!followingStatus) return "none"; + if (followingStatus.state.block) return "blocks"; + if (followingStatus.state.mute) return "mutes"; + if (followingStatus.state.follow) return "follows"; - return 'none' - } + return "none"; + }; - const searchTimeout = useRef(null) + const searchTimeout = useRef(null); const handleSearchEvent = (event: React.ChangeEvent) => { - const term = event?.target.value.toLowerCase() - if (!isEditor && term.includes(' ')) return - if (searchTimeout.current) clearTimeout(searchTimeout.current) + const term = event?.target.value.toLowerCase(); + if (!isEditor && term.includes(" ")) return; + if (searchTimeout.current) clearTimeout(searchTimeout.current); const hasMultipleNames = - isEditor && (term.includes(',') || term.includes(' ') || term.includes('\n')) - setDropdownMenuOpen(!hasMultipleNames && term.length > 0) - setCurrentSearch(term) + isEditor && (term.includes(",") || term.includes(" ") || term.includes("\n")); + setDropdownMenuOpen(!hasMultipleNames && term.length > 0); + setCurrentSearch(term); if (!isEditor) { - if (term) searchTimeout.current = setTimeout(() => setSearch(term), 0.5 * SECOND) + if (term) searchTimeout.current = setTimeout(() => setSearch(term), 0.5 * SECOND); else { - setSearch('') - router.push(pathname.replace('query=', '')) + setSearch(""); + router.push(pathname.replace("query=", "")); } } - } + }; const addToCart = async (user: string) => { if (!roles?.isManager) { - toast.error(t('not manager')) - return + toast.error(t("not manager")); + return; } - const address = isAddress(user) ? user : await resolveEnsAddress(user) + const address = isAddress(user) ? user : await resolveEnsAddress(user); if (!address) { - setLoadingCartItems(prevLoading => (prevLoading > 0 ? prevLoading - 1 : prevLoading)) - return { user } + setLoadingCartItems((prevLoading) => (prevLoading > 0 ? prevLoading - 1 : prevLoading)); + return { user }; } - const followState = await getFollowingState(address) - const isPendingFollow = hasListOpAddRecord(address) + const followState = await getFollowingState(address); + const isPendingFollow = hasListOpAddRecord(address); if (isPendingFollow) { - setLoadingCartItems(prevLoading => (prevLoading > 0 ? prevLoading - 1 : prevLoading)) - return { user, isFollowing: false, inCart: true } + setLoadingCartItems((prevLoading) => (prevLoading > 0 ? prevLoading - 1 : prevLoading)); + return { user, isFollowing: false, inCart: true }; } - if (followState === 'follows') { - setLoadingCartItems(prevLoading => (prevLoading > 0 ? prevLoading - 1 : prevLoading)) - return { user, isFollowing: true } + if (followState === "follows") { + setLoadingCartItems((prevLoading) => (prevLoading > 0 ? prevLoading - 1 : prevLoading)); + return { user, isFollowing: true }; } - if (followState === 'none') addCartItem({ listOp: listOpAddListRecord(address) }) - } + if (followState === "none") addCartItem({ listOp: listOpAddListRecord(address) }); + }; const onSubmit = async () => { if (isEditor) { - resetSearch() - searchBarRef.current?.focus() + resetSearch(); + searchBarRef.current?.focus(); - if (!roles?.isManager) return toast.error(t('not manager')) + if (!roles?.isManager) return toast.error(t("not manager")); - setIsAddingToCart(true) + setIsAddingToCart(true); const hasMultipleNames = isEditor && - (currentSearch.includes(',') || currentSearch.includes(' ') || currentSearch.includes('\n')) + (currentSearch.includes(",") || + currentSearch.includes(" ") || + currentSearch.includes("\n")); if (hasMultipleNames) { const namesToAdd = currentSearch - .replaceAll(',', ' ') - .replaceAll('\n', ' ') - .split(' ') - .map(name => name.trim()) - .filter(name => !!name) + .replaceAll(",", " ") + .replaceAll("\n", " ") + .split(" ") + .map((name) => name.trim()) + .filter((name) => !!name); - setLoadingCartItems(namesToAdd.length) + setLoadingCartItems(namesToAdd.length); - const addedToCart = await Promise.all(namesToAdd.map(async name => await addToCart(name))) + const addedToCart = await Promise.all( + namesToAdd.map(async (name) => await addToCart(name)) + ); - const namesInCart = addedToCart.filter(item => item?.inCart).map(item => item?.user) + const namesInCart = addedToCart.filter((item) => item?.inCart).map((item) => item?.user); const alreadyFollowed = addedToCart - .filter(item => item?.isFollowing) - .map(item => item?.user) + .filter((item) => item?.isFollowing) + .map((item) => item?.user); const erroredNames = addedToCart - .filter(item => !(item?.inCart || item?.isFollowing) && !!item?.user) - .map(item => item?.user) + .filter((item) => !(item?.inCart || item?.isFollowing) && !!item?.user) + .map((item) => item?.user); - if (erroredNames.length > 0) toast.error(`${t('unresolved')} ${formatError(erroredNames)}`) - if (namesInCart.length > 0) toast.error(`${t('in cart')} ${formatError(namesInCart)}`) + if (erroredNames.length > 0) toast.error(`${t("unresolved")} ${formatError(erroredNames)}`); + if (namesInCart.length > 0) toast.error(`${t("in cart")} ${formatError(namesInCart)}`); if (alreadyFollowed.length > 0) - toast.error(`${t('already followed')} ${formatError(alreadyFollowed)}`) + toast.error(`${t("already followed")} ${formatError(alreadyFollowed)}`); - return setIsAddingToCart(false) + return setIsAddingToCart(false); } - setLoadingCartItems(1) - const erroredName = await addToCart(currentSearch) - if (erroredName?.isFollowing) toast.error(`${t('already followed')} ${erroredName.user}`) - else if (erroredName?.inCart) toast.error(`${t('in cart')} ${erroredName.user}`) - else if (erroredName) toast.error(`${t('unresolved')} ${erroredName?.user}`) + setLoadingCartItems(1); + const erroredName = await addToCart(currentSearch); + if (erroredName?.isFollowing) toast.error(`${t("already followed")} ${erroredName.user}`); + else if (erroredName?.inCart) toast.error(`${t("in cart")} ${erroredName.user}`); + else if (erroredName) toast.error(`${t("unresolved")} ${erroredName?.user}`); - return setIsAddingToCart(false) + return setIsAddingToCart(false); } if ( !Number.isNaN(Number(currentSearch)) || - (currentSearch[0] === '#' && !Number.isNaN(Number(currentSearch.slice(1)))) + (currentSearch[0] === "#" && !Number.isNaN(Number(currentSearch.slice(1)))) ) { - router.push(`/${currentSearch[0] === '#' ? currentSearch.slice(1) : currentSearch}`) - resetSearch() + router.push(`/${currentSearch[0] === "#" ? currentSearch.slice(1) : currentSearch}`); + resetSearch(); } - if (isAddress(currentSearch) || currentSearch.includes('.')) { + if (isAddress(currentSearch) || currentSearch.includes(".")) { const address = isAddress(currentSearch) ? currentSearch - : await resolveEnsAddress(currentSearch) + : await resolveEnsAddress(currentSearch); router.push( - `/${address || currentSearch}${isAddress(currentSearch) ? '' : `?search=${currentSearch}`}` - ) - resetSearch() + `/${address || currentSearch}${isAddress(currentSearch) ? "" : `?search=${currentSearch}`}` + ); + resetSearch(); } - } + }; return { router, @@ -262,8 +266,8 @@ const useSearch = (isEditor?: boolean) => { isAddingToCart, dropdownMenuOpen, handleSearchEvent, - setDropdownMenuOpen - } -} + setDropdownMenuOpen, + }; +}; -export default useSearch +export default useSearch; diff --git a/src/components/search/index.tsx b/src/components/search/index.tsx index 4e4e036e..c4ffce22 100644 --- a/src/components/search/index.tsx +++ b/src/components/search/index.tsx @@ -164,7 +164,7 @@ export function Search({ {t('search no results')}
) : ( - searchResult.map(result => ( + searchResult.map((result) => (
{ @@ -259,7 +259,7 @@ export function Search({ {t('search no results')}
) : ( - searchResult.map(result => ( + searchResult.map((result) => (
{ diff --git a/src/components/top-eight/hooks/use-edit-top-eight.ts b/src/components/top-eight/hooks/use-edit-top-eight.ts index 71aa6d24..3569bd18 100644 --- a/src/components/top-eight/hooks/use-edit-top-eight.ts +++ b/src/components/top-eight/hooks/use-edit-top-eight.ts @@ -1,28 +1,28 @@ -import { toast } from 'sonner' -import { useAccount } from 'wagmi' -import { isAddress, type Address } from 'viem' -import { useTranslation } from 'react-i18next' -import { useEffect, useMemo, useState } from 'react' -import { fetchFollowState } from 'ethereum-identity-kit' +import { toast } from "sonner"; +import { useAccount } from "wagmi"; +import { isAddress, type Address } from "viem"; +import { useTranslation } from "react-i18next"; +import { useEffect, useMemo, useState } from "react"; +import { fetchFollowState } from "ethereum-identity-kit"; import { isTagListOp, listOpAddTag, listOpAddListRecord, - extractAddressAndTag -} from '#/utils/list-ops' -import { resolveEnsAddress } from '#/utils/ens' -import type { TagListOp } from '#/types/list-op' -import { useCart } from '#/contexts/cart-context' -import type { TopEightProfileType } from './use-top-eight' -import { useEFPProfile } from '#/contexts/efp-profile-context' + extractAddressAndTag, +} from "#/utils/list-ops"; +import { resolveEnsAddress } from "#/utils/ens"; +import type { TagListOp } from "#/types/list-op"; +import { useCart } from "#/contexts/cart-context"; +import type { TopEightProfileType } from "./use-top-eight"; +import { useEFPProfile } from "#/contexts/efp-profile-context"; export const useEditTopEight = (profiles: TopEightProfileType[]) => { - const { t } = useTranslation() - const { address: userAddress } = useAccount() - const { roles, selectedList } = useEFPProfile() + const { t } = useTranslation(); + const { address: userAddress } = useAccount(); + const { roles, selectedList } = useEFPProfile(); - const { cartItems, addCartItem, setLoadingCartItems } = useCart() + const { cartItems, addCartItem, setLoadingCartItems } = useCart(); const topEightInCart = useMemo( () => cartItems @@ -30,81 +30,83 @@ export const useEditTopEight = (profiles: TopEightProfileType[]) => { ({ listOp }) => listOp.opcode === 3 && isTagListOp(listOp) && - extractAddressAndTag(listOp).tag === 'top8' + extractAddressAndTag(listOp).tag === "top8" ) .map(({ listOp }) => ({ - address: extractAddressAndTag(listOp as TagListOp).address + address: extractAddressAndTag(listOp as TagListOp).address, })), [cartItems] - ) + ); - const [editedProfiles, setEditedProfiles] = useState([...profiles, ...topEightInCart]) + const [editedProfiles, setEditedProfiles] = useState([...profiles, ...topEightInCart]); const currentTopEightLength = useMemo(() => { const topEightRemoved = cartItems.filter( ({ listOp }) => - listOp.opcode === 4 && isTagListOp(listOp) && extractAddressAndTag(listOp).tag === 'top8' - ) + listOp.opcode === 4 && isTagListOp(listOp) && extractAddressAndTag(listOp).tag === "top8" + ); - return editedProfiles.length - topEightRemoved.length - }, [editedProfiles]) - const isTopEightFull = currentTopEightLength >= 8 + return editedProfiles.length - topEightRemoved.length; + }, [editedProfiles]); + const isTopEightFull = currentTopEightLength >= 8; useEffect(() => { - setEditedProfiles([...profiles, ...topEightInCart]) - }, [topEightInCart]) + setEditedProfiles([...profiles, ...topEightInCart]); + }, [topEightInCart]); const getFollowingState = async (address: Address) => { - if (!userAddress) return 'none' + if (!userAddress) return "none"; const followingStatus = await fetchFollowState({ lookupAddressOrName: address, connectedAddress: userAddress, list: selectedList, - type: 'following' - }) + type: "following", + }); - if (!followingStatus) return 'none' - if (followingStatus.state.block) return 'blocks' - if (followingStatus.state.mute) return 'mutes' - if (followingStatus.state.follow) return 'follows' + if (!followingStatus) return "none"; + if (followingStatus.state.block) return "blocks"; + if (followingStatus.state.mute) return "mutes"; + if (followingStatus.state.follow) return "follows"; - return 'none' - } + return "none"; + }; const addToCart = async (user: string) => { if (!roles?.isManager) { - toast.error(t('not manager')) - return + toast.error(t("not manager")); + return; } - setLoadingCartItems(prevLoading => prevLoading + 1) + setLoadingCartItems((prevLoading) => prevLoading + 1); - const address = isAddress(user) ? user : await resolveEnsAddress(user) - if (editedProfiles.find(profile => profile.address.toLowerCase() === address?.toLowerCase())) - return setLoadingCartItems(prevLoading => (prevLoading > 0 ? prevLoading - 1 : prevLoading)) + const address = isAddress(user) ? user : await resolveEnsAddress(user); + if (editedProfiles.find((profile) => profile.address.toLowerCase() === address?.toLowerCase())) + return setLoadingCartItems((prevLoading) => + prevLoading > 0 ? prevLoading - 1 : prevLoading + ); if (!address) { - setLoadingCartItems(prevLoading => (prevLoading > 0 ? prevLoading - 1 : prevLoading)) - return { user } + setLoadingCartItems((prevLoading) => (prevLoading > 0 ? prevLoading - 1 : prevLoading)); + return { user }; } - const followState = await getFollowingState(address) - if (followState === 'none') addCartItem({ listOp: listOpAddListRecord(address) }) - addCartItem({ listOp: listOpAddTag(address, 'top8') }) + const followState = await getFollowingState(address); + if (followState === "none") addCartItem({ listOp: listOpAddListRecord(address) }); + addCartItem({ listOp: listOpAddTag(address, "top8") }); - setLoadingCartItems(prevLoading => (prevLoading > 0 ? prevLoading - 1 : prevLoading)) - } + setLoadingCartItems((prevLoading) => (prevLoading > 0 ? prevLoading - 1 : prevLoading)); + }; - const [addProfileSearch, setAddProfileSearch] = useState('') + const [addProfileSearch, setAddProfileSearch] = useState(""); const onSubmit = async () => { - if (isTopEightFull) return toast.error(t('top eight limit')) - if (!roles?.isManager) return toast.error(t('not manager')) + if (isTopEightFull) return toast.error(t("top eight limit")); + if (!roles?.isManager) return toast.error(t("not manager")); - setAddProfileSearch('') - const addedToCart = await addToCart(addProfileSearch) - if (addedToCart?.user) toast.error(`${t('unresolved')} ${addProfileSearch}`) - } + setAddProfileSearch(""); + const addedToCart = await addToCart(addProfileSearch); + if (addedToCart?.user) toast.error(`${t("unresolved")} ${addProfileSearch}`); + }; return { onSubmit, @@ -112,6 +114,6 @@ export const useEditTopEight = (profiles: TopEightProfileType[]) => { isTopEightFull, editedProfiles, addProfileSearch, - setAddProfileSearch - } -} + setAddProfileSearch, + }; +}; diff --git a/src/contexts/efp-profile-context.tsx b/src/contexts/efp-profile-context.tsx index efd597cc..c2fcbf44 100644 --- a/src/contexts/efp-profile-context.tsx +++ b/src/contexts/efp-profile-context.tsx @@ -417,16 +417,16 @@ export const EFPProfileProvider: React.FC = ({ children }) => { const followers = fetchedFollowers ? fetchedFollowers.pages.reduce( - (acc, el) => [...acc, ...el.followers], - [] as FollowerResponse[] - ) + (acc, el) => [...acc, ...el.followers], + [] as FollowerResponse[] + ) : [] const following = fetchedFollowing ? fetchedFollowing.pages.reduce( - (acc, el) => [...acc, ...el.following], - [] as FollowingResponse[] - ) + (acc, el) => [...acc, ...el.following], + [] as FollowingResponse[] + ) : [] const { diff --git a/src/hooks/use-follower-state.ts b/src/hooks/use-follower-state.ts index 0ebdec2e..8f82e931 100644 --- a/src/hooks/use-follower-state.ts +++ b/src/hooks/use-follower-state.ts @@ -1,79 +1,79 @@ -import { useMemo } from 'react' -import { useAccount } from 'wagmi' -import type { Address } from 'viem' -import { useQuery } from '@tanstack/react-query' -import { fetchFollowState } from 'ethereum-identity-kit' +import { useMemo } from "react"; +import { useAccount } from "wagmi"; +import type { Address } from "viem"; +import { useQuery } from "@tanstack/react-query"; +import { fetchFollowState } from "ethereum-identity-kit"; -import type { FollowState } from '#/types/common' -import { useEFPProfile } from '#/contexts/efp-profile-context' +import type { FollowState } from "#/types/common"; +import { useEFPProfile } from "#/contexts/efp-profile-context"; const useFollowerState = ({ address, - showFollowerBadge = true + showFollowerBadge = true, }: { - address?: Address - showFollowerBadge?: boolean + address?: Address; + showFollowerBadge?: boolean; }) => { - const { selectedList } = useEFPProfile() - const { address: userAddress } = useAccount() + const { selectedList } = useEFPProfile(); + const { address: userAddress } = useAccount(); const { data: followerStatus, isLoading: isFollowerStatusLoading, - isRefetching: isFollowerStateRefetching + isRefetching: isFollowerStateRefetching, } = useQuery({ - queryKey: ['follower state', address, selectedList, userAddress], + queryKey: ["follower state", address, selectedList, userAddress], queryFn: async () => { - if (!(address && showFollowerBadge)) return null - if (!userAddress) return null + if (!(address && showFollowerBadge)) return null; + if (!userAddress) return null; const fetchedStatus = await fetchFollowState({ lookupAddressOrName: address, connectedAddress: userAddress, list: selectedList, - type: 'follower' - }) + type: "follower", + }); - return fetchedStatus + return fetchedStatus; }, - staleTime: Infinity - }) + staleTime: Infinity, + }); const followState = useMemo((): FollowState => { - if (!followerStatus?.state) return 'none' + if (!followerStatus?.state) return "none"; - if (followerStatus.state.block) return 'blocks' - if (followerStatus.state.mute) return 'mutes' - if (followerStatus.state.follow) return 'follows' + if (followerStatus.state.block) return "blocks"; + if (followerStatus.state.mute) return "mutes"; + if (followerStatus.state.follow) return "follows"; - return 'none' - }, [followerStatus]) + return "none"; + }, [followerStatus]); - const isFollowerStateLoading = isFollowerStatusLoading || isFollowerStateRefetching + const isFollowerStateLoading = isFollowerStatusLoading || isFollowerStateRefetching; const followerTag = { blocks: { - text: 'blocks you', - className: 'text-red-500' + text: "blocks you", + className: "text-red-500", }, mutes: { - text: 'mutes you', - className: 'text-red-500' + text: "mutes you", + className: "text-red-500", }, follows: { - text: 'follows you', - className: 'text-darkGray' + text: "follows you", + className: "text-darkGray", }, none: { - text: '', - className: 'hidden text-darkGray' - } - }[followState] + text: "", + className: "hidden text-darkGray", + }, + }[followState]; return { followState, followerTag, - isFollowerStateLoading - } -} + isFollowerStateLoading, + }; +}; -export default useFollowerState +export default useFollowerState; diff --git a/src/hooks/use-following-state.ts b/src/hooks/use-following-state.ts index 880600b2..b2e1ee26 100644 --- a/src/hooks/use-following-state.ts +++ b/src/hooks/use-following-state.ts @@ -1,76 +1,76 @@ -import { useMemo } from 'react' -import { useAccount } from 'wagmi' -import type { Address } from 'viem' -import { useQuery } from '@tanstack/react-query' -import { fetchFollowState } from 'ethereum-identity-kit' +import { useMemo } from "react"; +import { useAccount } from "wagmi"; +import type { Address } from "viem"; +import { useQuery } from "@tanstack/react-query"; +import { fetchFollowState } from "ethereum-identity-kit"; -import type { FollowState } from '#/types/common' -import { useEFPProfile } from '#/contexts/efp-profile-context' +import type { FollowState } from "#/types/common"; +import { useEFPProfile } from "#/contexts/efp-profile-context"; const useFollowingState = ({ address }: { address?: Address }) => { - const { address: userAddress } = useAccount() - const { selectedList, fetchFreshStats, listsIsLoading } = useEFPProfile() + const { address: userAddress } = useAccount(); + const { selectedList, fetchFreshStats, listsIsLoading } = useEFPProfile(); const { data: followingStatus, isLoading: isFollowingStatusLoading, - isRefetching: isFollowingStatusRefetching + isRefetching: isFollowingStatusRefetching, } = useQuery({ - queryKey: ['follow state', address, selectedList, fetchFreshStats, listsIsLoading], + queryKey: ["follow state", address, selectedList, fetchFreshStats, listsIsLoading], queryFn: async () => { - if (!address || listsIsLoading) return null - if (!userAddress) return null + if (!address || listsIsLoading) return null; + if (!userAddress) return null; const fetchedProfile = await fetchFollowState({ lookupAddressOrName: address, connectedAddress: userAddress, list: selectedList, - type: 'following', - fresh: fetchFreshStats - }) - return fetchedProfile + type: "following", + fresh: fetchFreshStats, + }); + return fetchedProfile; }, staleTime: Infinity, - refetchOnWindowFocus: false - }) + refetchOnWindowFocus: false, + }); const followingState = useMemo((): FollowState => { - if (!followingStatus?.state) return 'none' + if (!followingStatus?.state) return "none"; - if (followingStatus.state.block) return 'blocks' - if (followingStatus.state.mute) return 'mutes' - if (followingStatus.state.follow) return 'follows' + if (followingStatus.state.block) return "blocks"; + if (followingStatus.state.mute) return "mutes"; + if (followingStatus.state.follow) return "follows"; - return 'none' - }, [followingStatus]) + return "none"; + }, [followingStatus]); const isFollowingStateLoading = - isFollowingStatusLoading || isFollowingStatusRefetching || listsIsLoading + isFollowingStatusLoading || isFollowingStatusRefetching || listsIsLoading; const followerTag = { blocks: { - text: 'blocks you', - className: 'text-red-500' + text: "blocks you", + className: "text-red-500", }, mutes: { - text: 'mutes you', - className: 'text-red-500' + text: "mutes you", + className: "text-red-500", }, follows: { - text: 'follows you', - className: 'text-darkGray' + text: "follows you", + className: "text-darkGray", }, none: { - text: '', - className: 'hidden text-darkGray' - } - }[followingState] + text: "", + className: "hidden text-darkGray", + }, + }[followingState]; return { followingState, followerTag, - isFollowingStateLoading - } -} + isFollowingStateLoading, + }; +}; -export default useFollowingState +export default useFollowingState; diff --git a/src/lib/constants/integrations.ts b/src/lib/constants/integrations.ts index f0335268..14bd0ed1 100644 --- a/src/lib/constants/integrations.ts +++ b/src/lib/constants/integrations.ts @@ -27,6 +27,7 @@ import Snapshot from 'public/assets/partners/snapshot.jpeg' import BanklessAcademy from 'public/assets/partners/banklessacademy.jpeg' import ENSResolver from 'public/assets/partners/ensresolver.jpeg' + export const INTEGRATIONS = [ { name: 'Interface', diff --git a/src/types/requests.ts b/src/types/requests.ts index f56e9e95..4c1f2d8b 100644 --- a/src/types/requests.ts +++ b/src/types/requests.ts @@ -1,299 +1,299 @@ -import type { Address } from 'viem' -import type { LeaderboardFilter } from './common' -import type { StaticImageData } from 'next/image' +import type { Address } from "viem"; +import type { LeaderboardFilter } from "./common"; +import type { StaticImageData } from "next/image"; declare global { interface Window { // access via `window._APP_VERSION_`. The value is commit hash - readonly _APP_VERSION_: string + readonly _APP_VERSION_: string; } } export interface ENSProfile { - name?: string - avatar?: string - display?: string - header?: string - contenthash?: string - records?: Record - chains?: { [key: string]: string } - fresh?: number - resolver?: string - errors?: { [key: string]: string } + name?: string; + avatar?: string; + display?: string; + header?: string; + contenthash?: string; + records?: Record; + chains?: { [key: string]: string }; + fresh?: number; + resolver?: string; + errors?: { [key: string]: string }; } export interface ENSMetadataProfile { - uri: string - is_owner: boolean - full_image: string - full_svg: string - svg: string + uri: string; + is_owner: boolean; + full_image: string; + full_svg: string; + svg: string; host_meta: { - chain_id: string | number - namespace: string - contract_address: string - token_id: string | number - reference_url: string - } - name: string - description: string - attribute: string - image: string - image_url: string - image_data: string - background_color: string - youtube_url: string -} - -export type FollowSortType = 'latest first' | 'earliest first' | 'follower count' + chain_id: string | number; + namespace: string; + contract_address: string; + token_id: string | number; + reference_url: string; + }; + name: string; + description: string; + attribute: string; + image: string; + image_url: string; + image_data: string; + background_color: string; + youtube_url: string; +} + +export type FollowSortType = "latest first" | "earliest first" | "follower count"; export interface InfiniteProfileQueryProps { - addressOrName: string - list?: number | string - limit: number - tags?: string[] - sort?: FollowSortType - pageParam: number - allResults?: boolean - search?: string - fresh?: boolean + addressOrName: string; + list?: number | string; + limit: number; + tags?: string[]; + sort?: FollowSortType; + pageParam: number; + allResults?: boolean; + search?: string; + fresh?: boolean; } -export type LeaderboardDirection = 'asc' | 'desc' +export type LeaderboardDirection = "asc" | "desc"; export interface InfiniteLeaderboardQueryProps { - limit: number - pageParam: number - search?: string | null - filter?: LeaderboardFilter - direction?: LeaderboardDirection + limit: number; + pageParam: number; + search?: string | null; + filter?: LeaderboardFilter; + direction?: LeaderboardDirection; } export interface FollowerResponse { - address: Address - ens: ENSProfile - tags: string[] - is_muted: boolean - is_blocked: boolean - is_following: boolean + address: Address; + ens: ENSProfile; + tags: string[]; + is_muted: boolean; + is_blocked: boolean; + is_following: boolean; } export interface LatestFollowersResponse { - address: Address - efp_list_nft_token_id: string - updated_at: string + address: Address; + efp_list_nft_token_id: string; + updated_at: string; } export interface FollowingResponse { - version: 1 - record_type: 'address' & string - address: Address - tags: string[] - ens?: ENSProfile + version: 1; + record_type: "address" & string; + address: Address; + tags: string[]; + ens?: ENSProfile; } export interface FollowStatusResponse { - token_id: string - address: Address + token_id: string; + address: Address; state: { - follow: boolean - block: boolean - mute: boolean - } + follow: boolean; + block: boolean; + mute: boolean; + }; } export type LeaderboardItem = { - address: Address - name: string | null - avatar: string | null - mutuals_rank: string - followers_rank: string - following_rank: string - top8_rank: string - blocks_rank: string - top8?: string - following?: string - followers: string - blocks?: string - mutuals?: string -} + address: Address; + name: string | null; + avatar: string | null; + mutuals_rank: string; + followers_rank: string; + following_rank: string; + top8_rank: string; + blocks_rank: string; + top8?: string; + following?: string; + followers: string; + blocks?: string; + mutuals?: string; +}; export interface LeaderboardResponse { - last_updated: string - results: LeaderboardItem[] + last_updated: string; + results: LeaderboardItem[]; } export interface LeaderboardStatsResponse { - address_count: string - list_count: string - list_op_count: string - user_count: string + address_count: string; + list_count: string; + list_op_count: string; + user_count: string; } export interface StatsResponse { - followers_count: number - following_count: number + followers_count: number; + following_count: number; } export interface ProfileResponse { - address: Address - ens: ENSProfile - fresh?: number - resolver?: string - primary_list?: string | null - stats?: StatsResponse | undefined - followers?: FollowerResponse[] - following?: FollowingResponse[] - chains?: Record - errors?: Record + address: Address; + ens: ENSProfile; + fresh?: number; + resolver?: string; + primary_list?: string | null; + stats?: StatsResponse | undefined; + followers?: FollowerResponse[]; + following?: FollowingResponse[]; + chains?: Record; + errors?: Record; } export type ProfileRanks = { - mutuals_rank: number - followers_rank: number - following_rank: number - blocks_rank: number - top8_rank: number -} + mutuals_rank: number; + followers_rank: number; + following_rank: number; + blocks_rank: number; + top8_rank: number; +}; export interface ProfileDetailsResponse { - address: Address - ens: ENSProfile - primary_list?: string | null - ranks: ProfileRanks + address: Address; + ens: ENSProfile; + primary_list?: string | null; + ranks: ProfileRanks; } export interface ProfileDetailsResponseWithStats extends ProfileDetailsResponse { - stats: StatsResponse + stats: StatsResponse; } export interface ProfileBadgesResponse { - eventId: string - participated: boolean - collection: ProfileBadgeColletionType | null + eventId: string; + participated: boolean; + collection: ProfileBadgeColletionType | null; } export interface ProfileBadgeColletionType { event: { - id: number - fancy_id: string - name: string - event_url: string - image_url: string - country: string - city: string - description: string - year: number - start_date: string - end_date: string - expiry_date: string - } - tokenId: string - owner: string + id: number; + fancy_id: string; + name: string; + event_url: string; + image_url: string; + country: string; + city: string; + description: string; + year: number; + start_date: string; + end_date: string; + expiry_date: string; + }; + tokenId: string; + owner: string; } export interface ProfileDetailsWithStats extends ProfileDetailsResponse { - stats: StatsResponse + stats: StatsResponse; } export type TagCountType = { - tag: string - count: number -} + tag: string; + count: number; +}; export interface FollowingTagsResponse { - token_id: string | number - tags: string[] - tagCounts: TagCountType[] + token_id: string | number; + tags: string[]; + tagCounts: TagCountType[]; taggedAddresses: { - address: Address - tag: string - }[] + address: Address; + tag: string; + }[]; } export type CommonFollower = { - address: Address - name: string | null - avatar: string | null - mutuals_rank: string -} + address: Address; + name: string | null; + avatar: string | null; + mutuals_rank: string; +}; export interface CommonFollowersResponse { - results: CommonFollower[] - length: number + results: CommonFollower[]; + length: number; } export interface ProfileListsResponse { - primary_list?: string | null - lists?: string[] + primary_list?: string | null; + lists?: string[]; } export type ProfileRoles = { - isOwner: boolean - isManager: boolean - isUser: boolean - listChainId: number - listRecordsContract: Address - listSlot: bigint -} + isOwner: boolean; + isManager: boolean; + isUser: boolean; + listChainId: number; + listRecordsContract: Address; + listSlot: bigint; +}; export type AccountResponseType = { - address: Address + address: Address; ens: { - name: string | null - avatar: string | null - } - primary_list: string | null -} + name: string | null; + avatar: string | null; + }; + primary_list: string | null; +}; export type DiscoverItemType = { - address: Address - name: string | null - avatar: string | null - followers: number - following: number -} + address: Address; + name: string | null; + avatar: string | null; + followers: number; + following: number; +}; export type RecommendedItemType = { - address: Address - name: string | null - avatar: string | null -} + address: Address; + name: string | null; + avatar: string | null; +}; export type DiscoverResponseType = { - latestFollows: DiscoverItemType[] - recommended: RecommendedItemType[] -} + latestFollows: DiscoverItemType[]; + recommended: RecommendedItemType[]; +}; export type RecommendedProfilesResponseType = { - recommended: ProfileDetailsResponseWithStats[] -} + recommended: ProfileDetailsResponseWithStats[]; +}; -export type QRCodeResponse = StaticImageData +export type QRCodeResponse = StaticImageData; // Airstack export type AirstackProfileResponse = { data: { Socials: { Social: { - profileImage: string - profileHandle: string - profileName: string - userAddress: string - }[] - } - } -} + profileImage: string; + profileHandle: string; + profileName: string; + userAddress: string; + }[]; + }; + }; +}; export type AirstackFollowings = { - followingAddress: { addresses: Address[]; primaryDomain: { name: string } } -} + followingAddress: { addresses: Address[]; primaryDomain: { name: string } }; +}; export type AirstackFollowingsResponse = { data: { SocialFollowings: { - Following: AirstackFollowings[] - pageInfo: { nextCursor: string; hasPrevPage: boolean; hasNextPage: boolean } - } - } -} + Following: AirstackFollowings[]; + pageInfo: { nextCursor: string; hasPrevPage: boolean; hasNextPage: boolean }; + }; + }; +}; diff --git a/tsconfig.json b/tsconfig.json index ae17450d..8785277f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,5 @@ { + "schema": "https://json.schemastore.org/tsconfig.json", "display": "Default", "compilerOptions": { "strict": true, @@ -48,6 +49,6 @@ ".next/types/**/*.ts", "svgr.d.ts" ], - "files": ["environment.d.ts", "next.config.mjs", "env.ts"], + "files": ["biome.json", "environment.d.ts", "next.config.mjs", "env.ts"], "exclude": ["node_modules", "./src/lib/generated", "_"] }