Skip to content
This repository has been archived by the owner on Dec 21, 2023. It is now read-only.

Commit

Permalink
feat: top creator token (#2501)
Browse files Browse the repository at this point in the history
* feat: add top creator token module

* improvements

* update the old design of creator token

* update endpoint

* improvements
  • Loading branch information
alantoa authored Nov 7, 2023
1 parent d6ac2cb commit 1c6df10
Show file tree
Hide file tree
Showing 13 changed files with 438 additions and 136 deletions.
197 changes: 194 additions & 3 deletions packages/app/components/creator-token/creator-token-users.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,25 @@ import { Platform } from "react-native";

import { Avatar } from "@showtime-xyz/universal.avatar";
import { useIsDarkMode } from "@showtime-xyz/universal.hooks";
import { Showtime } from "@showtime-xyz/universal.icon";
import {
GoldHexagon,
Showtime,
ShowtimeRounded,
} from "@showtime-xyz/universal.icon";
import { PressableHover } from "@showtime-xyz/universal.pressable-hover";
import { useRouter } from "@showtime-xyz/universal.router";
import { Skeleton } from "@showtime-xyz/universal.skeleton";
import { colors } from "@showtime-xyz/universal.tailwind";
import { Text } from "@showtime-xyz/universal.text";
import { VerificationBadge } from "@showtime-xyz/universal.verification-badge";
import { View, ViewProps } from "@showtime-xyz/universal.view";

import { CreatorTokenUser } from "app/hooks/creator-token/use-creator-tokens";
import {
CreatorTokenUser,
TopCreatorTokenUser,
} from "app/hooks/creator-token/use-creator-tokens";
import { useHeaderHeight } from "app/lib/react-navigation/elements";
import { formatAddressShort } from "app/utilities";

export const CreatorTokensTitle = ({ title }: { title: string }) => {
const headerHeight = useHeaderHeight();
Expand All @@ -32,7 +42,7 @@ export const CreatorTokensTitle = ({ title }: { title: string }) => {
</View>
);
};
export const TopCreatorTokensItem = ({
export const CreatorTokenCard = ({
index,
tw,
item,
Expand Down Expand Up @@ -75,3 +85,184 @@ export const TopCreatorTokensItem = ({
</PressableHover>
);
};

export const TopCreatorTokenListItem = ({
index,
tw,
item,
showName = false,
...rest
}: ViewProps & {
index?: number;
item: CreatorTokenUser;
showName?: boolean;
}) => {
const router = useRouter();
return (
<PressableHover
tw={["py-1.5", tw].join(" ")}
onPress={() => router.push(`/@${item.username}`)}
{...rest}
>
<View tw="flex-row items-center">
{index != undefined ? (
index < 3 ? (
<View tw="mr-1 items-center justify-center">
<View tw="absolute -top-1">
<GoldHexagon width={18} height={18} />
</View>
<Text tw="text-xs font-bold text-white">{index + 1}</Text>
</View>
) : (
<View tw="mr-1 items-center justify-center">
<Text tw="text-xs font-bold text-gray-700 dark:text-white">
{index + 1}
</Text>
</View>
)
) : null}
<View tw="web:flex-1 ml-2 flex-row items-center">
<Avatar url={item?.img_url} size={34} />
<View tw="w-2" />
{showName ? (
<View tw="flex-1 justify-center">
{item.name ? (
<>
<Text
tw="text-sm font-semibold text-gray-600 dark:text-gray-300"
numberOfLines={1}
>
{item.name}
</Text>
<View tw="h-1" />
</>
) : null}

<View tw="flex-row items-center">
<Text
tw="text-sm font-semibold text-gray-900 dark:text-white"
numberOfLines={1}
>
{item.username ? (
<>@{item.username}</>
) : (
<>{formatAddressShort(item.wallet_address)}</>
)}
</Text>
{Boolean(item.verified) && (
<View tw="ml-1">
<VerificationBadge size={14} />
</View>
)}
</View>
</View>
) : (
<Text
tw="flex-1 text-sm font-semibold text-gray-900 dark:text-white"
numberOfLines={1}
>
@{item?.username}
</Text>
)}
</View>
</View>
</PressableHover>
);
};
export const TopCreatorTokenItem = ({
index,
tw,
item,
...rest
}: ViewProps & {
index?: number;
item: TopCreatorTokenUser;
}) => {
const router = useRouter();
const isDark = useIsDarkMode();
return (
<PressableHover
tw={["py-1.5", tw].join(" ")}
onPress={() => router.push(`/@${item.owner_profile.username}`)}
{...rest}
>
<View tw="flex-row items-center">
<View tw="min-w-[24px]">
{index != undefined ? (
index < 3 ? (
<View tw="items-center justify-center">
<View tw="absolute -top-1">
<GoldHexagon width={18} height={18} />
</View>
<Text tw="text-xs font-bold text-white">{index + 1}</Text>
</View>
) : (
<View tw="items-center justify-center">
<Text tw="text-xs font-bold text-gray-700 dark:text-white">
{index + 1}
</Text>
</View>
)
) : null}
</View>
<View tw="web:flex-1 ml-2 flex-row items-center">
<Avatar url={item?.owner_profile?.img_url} size={34} />
<View tw="w-2" />
<View tw="flex-1 justify-center">
<View tw="flex-row items-center">
{item?.owner_profile?.username ? (
<>
<Text
tw="text-sm font-semibold text-gray-900 dark:text-white"
numberOfLines={1}
>
@{item.owner_profile.username}
</Text>
<View tw="h-1" />
</>
) : null}
{Boolean(item.owner_profile?.verified) && (
<View tw="ml-1">
<VerificationBadge size={14} />
</View>
)}
</View>
<View tw="mt-1 flex-row items-center">
<Text
tw="text-xs font-semibold text-gray-900 dark:text-white"
numberOfLines={1}
>
{item.nft_count}
</Text>
<View tw="w-1" />
<ShowtimeRounded
width={14}
height={14}
color={isDark ? colors.white : colors.gray[900]}
/>
</View>
</View>
</View>
</View>
</PressableHover>
);
};

export const TopCreatorTokenSkeleton = ({ tw, ...rest }: ViewProps) => {
return (
<View tw={["py-1.5", tw].join(" ")} {...rest}>
<View tw="flex-row items-center pl-1">
<Skeleton width={16} height={16} show radius={8} />
<View tw="ml-2 flex-row items-center">
<Skeleton width={34} height={34} show radius={999} />
<View tw="w-2" />
<View tw="">
<Skeleton width={100} height={13} show radius={4} />
<View tw="h-1" />
<Skeleton width={80} height={13} show radius={4} />
</View>
</View>
</View>
</View>
);
};
118 changes: 42 additions & 76 deletions packages/app/components/creator-token/top-creator-token.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,69 +18,40 @@ import { EmptyPlaceholder } from "app/components/empty-placeholder";
import { ErrorBoundary } from "app/components/error-boundary";
import {
CreatorTokenUser,
TopCreatorTokenUser,
useCreatorTokenCollectors,
useTopCreatorToken,
} from "app/hooks/creator-token/use-creator-tokens";
import { useHeaderHeight } from "app/lib/react-navigation/elements";

import { breakpoints } from "design-system/theme";

import { TopCreatorTokensItem } from "./creator-token-users";

const Header = () => {
const headerHeight = useHeaderHeight();
return (
<>
<View
style={{
height: Platform.select({
ios: headerHeight + 8,
default: 8,
}),
}}
/>
<View tw="hidden flex-row justify-between bg-white pb-4 pt-6 dark:bg-black md:flex">
<Text tw="font-bold text-gray-900 dark:text-white md:text-xl">
Top Creator Tokens
</Text>
</View>
</>
);
};
import {
TopCreatorTokenItem,
TopCreatorTokenSkeleton,
} from "./creator-token-users";

const keyExtractor = (item: CreatorTokenUser) => `${item.profile_id}`;
const keyExtractor = (item: TopCreatorTokenUser) => `${item.id}`;
export const TopCreatorTokens = () => {
const { height: screenHeight, width } = useWindowDimensions();
const isMdWidth = width >= breakpoints["md"];
const { data: list, isLoading } = useCreatorTokenCollectors(27);
const { height: screenHeight } = useWindowDimensions();
const { data: list, isLoading, fetchMore } = useTopCreatorToken();

const numColumns = 3;
const numColumns = 1;

const renderItem = useCallback(
({
item,
index,
}: ListRenderItemInfo<CreatorTokenUser & { loading?: boolean }>) => {
return <TopCreatorTokensItem item={item} index={index} />;
}: ListRenderItemInfo<TopCreatorTokenUser & { loading?: boolean }>) => {
return <TopCreatorTokenItem item={item} index={index} />;
},
[]
);

const getItemType = useCallback(
(_: CreatorTokenUser, index: number) => {
const marginLeft = isMdWidth ? 0 : index % numColumns === 0 ? 0 : 8;
if (marginLeft) {
return "right";
}
return "left";
},
[isMdWidth, numColumns]
);

const ListEmptyComponent = useCallback(() => {
if (isLoading) {
if (!isLoading) {
return (
<View tw="mx-auto w-full max-w-screen-xl justify-center md:px-0">
<Spinner />
<View>
{new Array(6).fill(0).map((_, i) => {
return <TopCreatorTokenSkeleton key={i} />;
})}
</View>
);
}
Expand All @@ -94,35 +65,30 @@ export const TopCreatorTokens = () => {
}, [isLoading]);

return (
<View tw="min-h-screen w-full bg-white dark:bg-black">
<View tw="md:max-w-screen-content mx-auto w-full">
<ErrorBoundary>
<InfiniteScrollList
useWindowScroll
data={list || []}
preserveScrollPosition
keyExtractor={keyExtractor}
numColumns={numColumns}
renderItem={renderItem}
drawDistance={500}
getItemType={getItemType}
style={{
height: Platform.select({
web: undefined,
default: screenHeight,
}),
}}
contentContainerStyle={{
paddingHorizontal: 8,
}}
overscan={12}
containerTw="px-4 md:px-0"
ListEmptyComponent={ListEmptyComponent}
ListHeaderComponent={Header}
estimatedItemSize={275}
/>
</ErrorBoundary>
</View>
</View>
<ErrorBoundary>
<InfiniteScrollList
useWindowScroll
data={list || []}
preserveScrollPosition
keyExtractor={keyExtractor}
numColumns={numColumns}
renderItem={renderItem}
drawDistance={500}
style={{
height: Platform.select({
web: undefined,
default: screenHeight,
}),
}}
contentContainerStyle={{
paddingHorizontal: 12,
}}
overscan={12}
containerTw="px-4"
onEndReached={fetchMore}
ListEmptyComponent={ListEmptyComponent}
estimatedItemSize={46}
/>
</ErrorBoundary>
);
};
5 changes: 1 addition & 4 deletions packages/app/components/home/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,7 @@ export const ListHeaderComponent = memo(function ListHeaderComponent() {
)
)}
</View>
{/*
// TODO: Creator Tokens P1
<TopPartCreatorTokens />
*/}
<TopPartCreatorTokens />
<TrendingCarousel />
</View>
);
Expand Down
Loading

0 comments on commit 1c6df10

Please sign in to comment.