diff --git a/package.json b/package.json
index 5d1582fb95..1c96fec89c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "showtime",
- "version": "149.0.7",
+ "version": "149.0.12",
"private": true,
"license": "MIT",
"devDependencies": {
diff --git a/packages/app/components/creator-channels/channels.web.tsx b/packages/app/components/creator-channels/channels.web.tsx
index e7a760601b..eab49125c9 100644
--- a/packages/app/components/creator-channels/channels.web.tsx
+++ b/packages/app/components/creator-channels/channels.web.tsx
@@ -25,7 +25,6 @@ import { breakpoints } from "design-system/theme";
import {
useJoinedChannelsList,
useOwnedChannelsList,
- useSuggestedChannelsList,
} from "./hooks/use-channels-list";
import { useJoinChannel } from "./hooks/use-join-channel";
import { CreatorChannelsList as CreatorChannelsListMobile } from "./list";
@@ -46,12 +45,6 @@ const channelsSection = {
"Get exclusive updates, presale access and unreleased content from your favorite creators.",
};
-const suggestedChannelsSection = {
- type: "section",
- title: "Popular creators",
- tw: "text-xl",
-};
-
const CreatorChannelsHeader = memo(
({
title,
@@ -330,10 +323,6 @@ export const CreatorChannels = memo(() => {
isLoading: isLoadingJoinedChannels,
} = useJoinedChannelsList();
- // suggested channels
- const { data: suggestedChannelsData, isLoading: isLoadingSuggestedChannels } =
- useSuggestedChannelsList();
-
// since we're quering two different endpoints, and based on the amount of data from the first endpoint
// we have to transform our data a bit and decide if we build a section list or a single FlashList
// we're going to useMemo for that and return the data in the format we need
@@ -353,16 +342,6 @@ export const CreatorChannels = memo(() => {
...joinedChannelsData,
]
: []),
- // check if we have any suggested channels, if we do, we're going to add a section for them (+ the suggested channels)
- ...(suggestedChannelsData.length > 0
- ? [
- suggestedChannelsSection,
- ...suggestedChannelsData.map((suggestedChannel) => ({
- ...suggestedChannel,
- itemType: "creator",
- })),
- ]
- : []),
];
} else {
return [
@@ -378,11 +357,7 @@ export const CreatorChannels = memo(() => {
...joinedChannelsData,
];
}
- }, [
- joinedChannelsData,
- ownedChannelsData,
- suggestedChannelsData,
- ]) as CreatorChannelsListItemProps[];
+ }, [joinedChannelsData, ownedChannelsData]) as CreatorChannelsListItemProps[];
const renderItem = useCallback(({ item }: CreatorChannelsListProps) => {
if (item.type === "section") {
@@ -403,11 +378,7 @@ export const CreatorChannels = memo(() => {
}, []);
const ListFooterComponent = useCallback(() => {
- if (
- isLoadingJoinedChannels ||
- isLoadingOwnChannels ||
- isLoadingSuggestedChannels
- ) {
+ if (isLoadingJoinedChannels || isLoadingOwnChannels) {
return ;
}
@@ -427,7 +398,6 @@ export const CreatorChannels = memo(() => {
}, [
isLoadingJoinedChannels,
isLoadingOwnChannels,
- isLoadingSuggestedChannels,
isLoadingMoreJoinedChannels,
]);
if (!isLgWidth) {
@@ -463,9 +433,7 @@ export const CreatorChannels = memo(() => {
{
const router = useRouter();
const isDark = useIsDarkMode();
- const viewMembersList = useCallback(() => {
- const as = `/channels/${props.channelId}/members`;
-
- router.push(
- Platform.select({
- native: as,
- web: {
- pathname: router.pathname,
- query: {
- ...router.query,
- channelsMembersModal: true,
- },
- } as any,
- }),
- Platform.select({
- native: as,
- web: router.asPath,
- }),
- { shallow: true }
- );
- }, [props.channelId, router]);
-
const inviteAllowlist = useCallback(() => {
const as = "/creator-token/import-allowlist";
router.push(
@@ -112,8 +90,7 @@ export const MessagesHeader = (props: HeaderProps) => {
/>
) : (
- // TODO: Creator Tokens P1 (hide hidden class)
-
+
{
data: newData,
};
};
-
-export const useSuggestedChannelsList = (params?: { pageSize?: number }) => {
- const pageSize = params?.pageSize || PAGE_SIZE;
- const channelsFetcher = useCallback(
- (index: number, previousPageData: []) => {
- if (previousPageData && !previousPageData.length) return null;
- return `/v1/channels/suggested?page=${index + 1}&limit=${pageSize}`;
- },
- [pageSize]
- );
-
- const queryState = useInfiniteListQuerySWR(
- channelsFetcher,
- {
- pageSize,
- }
- );
- const newData = useMemo(() => {
- let newData: CreatorChannel[] = [];
- if (queryState.data) {
- queryState.data.forEach((p) => {
- if (p) {
- newData = newData.concat(p);
- }
- });
- }
- return newData;
- }, [queryState.data]);
-
- return {
- ...queryState,
- data: newData,
- };
-};
diff --git a/packages/app/components/creator-channels/hooks/use-join-channel.ts b/packages/app/components/creator-channels/hooks/use-join-channel.ts
index 9f1b86d7f2..332a4fc15c 100644
--- a/packages/app/components/creator-channels/hooks/use-join-channel.ts
+++ b/packages/app/components/creator-channels/hooks/use-join-channel.ts
@@ -7,10 +7,7 @@ import { Logger } from "app/lib/logger";
import { useLogInPromise } from "app/lib/login-promise";
import { captureException } from "app/lib/sentry";
-import {
- useJoinedChannelsList,
- useSuggestedChannelsList,
-} from "./use-channels-list";
+import { useJoinedChannelsList } from "./use-channels-list";
async function joinChannel(
url: string,
@@ -34,7 +31,6 @@ export const useJoinChannel = () => {
joinChannel
);
const joinedChannels = useJoinedChannelsList();
- const suggestedChannels = useSuggestedChannelsList();
const { onboardingPromise } = useOnboardingPromise();
const handleSubmit = useStableCallback(
@@ -47,7 +43,6 @@ export const useJoinChannel = () => {
captureException(e);
Logger.error(e);
} finally {
- suggestedChannels.mutate();
joinedChannels.mutate();
}
}
diff --git a/packages/app/components/creator-channels/hooks/use-leave-channel.ts b/packages/app/components/creator-channels/hooks/use-leave-channel.ts
index 2e089c7911..df433a5135 100644
--- a/packages/app/components/creator-channels/hooks/use-leave-channel.ts
+++ b/packages/app/components/creator-channels/hooks/use-leave-channel.ts
@@ -5,10 +5,7 @@ import { axios } from "app/lib/axios";
import { Logger } from "app/lib/logger";
import { captureException } from "app/lib/sentry";
-import {
- useJoinedChannelsList,
- useSuggestedChannelsList,
-} from "./use-channels-list";
+import { useJoinedChannelsList } from "./use-channels-list";
async function leaveChannel(
url: string,
@@ -26,7 +23,6 @@ export const useLeaveChannel = () => {
leaveChannel
);
const joinedChannels = useJoinedChannelsList();
- const suggestedChannels = useSuggestedChannelsList();
const handleSubmit = useStableCallback(
async ({ channelId }: { channelId: string }) => {
@@ -37,7 +33,6 @@ export const useLeaveChannel = () => {
Logger.error(e);
} finally {
joinedChannels.mutate();
- suggestedChannels.mutate();
}
}
);
diff --git a/packages/app/components/creator-channels/list.tsx b/packages/app/components/creator-channels/list.tsx
index 15469f975d..bfd466a06a 100644
--- a/packages/app/components/creator-channels/list.tsx
+++ b/packages/app/components/creator-channels/list.tsx
@@ -38,7 +38,6 @@ import { LeanText } from "./components/lean-text";
import {
useJoinedChannelsList,
useOwnedChannelsList,
- useSuggestedChannelsList,
} from "./hooks/use-channels-list";
import { useJoinChannel } from "./hooks/use-join-channel";
import {
@@ -340,12 +339,6 @@ const channelsSection = {
"Get exclusive updates, presale access and unreleased content from your favorite creators.",
};
-const suggestedChannelsSection = {
- type: "section",
- title: "Popular creators",
- tw: "text-xl",
-};
-
export const CreatorChannelsList = memo(
({ useWindowScroll = true }: { useWindowScroll?: boolean }) => {
const listRef = useRef>(null);
@@ -376,14 +369,6 @@ export const CreatorChannelsList = memo(
isLoading: isLoadingJoinedChannels,
} = useJoinedChannelsList();
- // suggested channels
- const {
- data: suggestedChannelsData,
- refresh: refreshSuggestedChannels,
- isLoading: isLoadingSuggestedChannels,
- isRefreshing: isRefreshingSuggestedChannels,
- } = useSuggestedChannelsList();
-
// since we're quering two different endpoints, and based on the amount of data from the first endpoint
// we have to transform our data a bit and decide if we build a section list or a single FlashList
// we're going to useMemo for that and return the data in the format we need
@@ -403,16 +388,6 @@ export const CreatorChannelsList = memo(
...joinedChannelsData,
]
: []),
- // check if we have any suggested channels, if we do, we're going to add a section for them (+ the suggested channels)
- ...(suggestedChannelsData.length > 0
- ? [
- suggestedChannelsSection,
- ...suggestedChannelsData.map((suggestedChannel) => ({
- ...suggestedChannel,
- itemType: "creator",
- })),
- ]
- : []),
];
} else {
return [
@@ -431,7 +406,6 @@ export const CreatorChannelsList = memo(
}, [
joinedChannelsData,
ownedChannelsData,
- suggestedChannelsData,
]) as CreatorChannelsListItemProps[];
const renderItem = useCallback(({ item }: CreatorChannelsListProps) => {
@@ -479,17 +453,9 @@ export const CreatorChannelsList = memo(
]);
const refreshPage = useCallback(async () => {
- await Promise.all([
- refresh(),
- refreshOwnedChannels(),
- refreshSuggestedChannels(),
- ]);
- }, [refresh, refreshOwnedChannels, refreshSuggestedChannels]);
- if (
- isLoadingOwnChannels ||
- isLoadingJoinedChannels ||
- isLoadingSuggestedChannels
- ) {
+ await Promise.all([refresh(), refreshOwnedChannels()]);
+ }, [refresh, refreshOwnedChannels]);
+ if (isLoadingOwnChannels || isLoadingJoinedChannels) {
return ;
}
return (
@@ -521,11 +487,7 @@ export const CreatorChannelsList = memo(
// Todo: unity refresh control same as tab view
refreshControl={
();
// Disable ETH payment on dev for now because it doesn't support the dev environment yet.
-const PAYMENT_METHODS = __DEV__
- ? [
- {
- title: "USDC",
- value: "USDC",
- },
- ]
- : [
- {
- title: "ETH",
- value: "ETH",
- },
- {
- title: "USDC",
- value: "USDC",
- },
- ];
+const BUY_PAYMENTS = [
+ {
+ title: "ETH",
+ value: "ETH",
+ },
+ {
+ title: "USDC",
+ value: "USDC",
+ },
+];
+const SELL_PAYMENTS = [
+ {
+ title: "USDC",
+ value: "USDC",
+ },
+];
+
const SELECT_LIST = [
{
title: "Buy",
@@ -168,6 +168,7 @@ export const BuyCreatorToken = () => {
"https://app.uniswap.org/swap?outputCurrency=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913&chain=base"
)
}
+ size="regular"
>
Buy USDC on Uniswap
@@ -180,6 +181,7 @@ export const BuyCreatorToken = () => {
return (
@@ -262,15 +264,21 @@ export const BuyCreatorToken = () => {
- m.value === "USDC")
- }
- value={paymentMethod}
- onChange={(value: any) => setPaymentMethod(value)}
- />
+ {selectedAction === "buy" ? (
+ setPaymentMethod(value)}
+ key="BUY_PAYMENTS"
+ />
+ ) : (
+ setPaymentMethod(value)}
+ key="SELL_PAYMENTS"
+ />
+ )}
{
setShowExplanation(true);
diff --git a/packages/app/components/creator-token/self-serve-explainer.tsx b/packages/app/components/creator-token/self-serve-explainer.tsx
index 82a8636113..e34763d2e9 100644
--- a/packages/app/components/creator-token/self-serve-explainer.tsx
+++ b/packages/app/components/creator-token/self-serve-explainer.tsx
@@ -36,7 +36,9 @@ export const SelfServeExplainer = () => {
const isDark = useIsDarkMode();
const { user } = useUser();
const { top } = useSafeAreaInsets();
- const userProfilePic = user?.data.profile.img_url;
+ const userProfilePic =
+ user?.data.profile.img_url ||
+ "https://media.showtime.xyz/assets/default-creator-token-pic.png";
const [profilePic, setProfilePic] = useState(
userProfilePic
diff --git a/packages/app/components/footer/index.tsx b/packages/app/components/footer/index.tsx
index 0ded471b5c..767ee1de12 100644
--- a/packages/app/components/footer/index.tsx
+++ b/packages/app/components/footer/index.tsx
@@ -1,3 +1,4 @@
+import { useContext } from "react";
import { useWindowDimensions } from "react-native";
import { useIsDarkMode } from "@showtime-xyz/universal.hooks";
@@ -5,6 +6,7 @@ import { useRouter } from "@showtime-xyz/universal.router";
import { View } from "@showtime-xyz/universal.view";
import { MOBILE_WEB_TABS_HEIGHT } from "app/constants/layout";
+import { UserContext } from "app/context/user-context";
import {
HIDE_MOBILE_WEB_FOOTER_SCREENS,
SWIPE_LIST_SCREENS,
@@ -19,6 +21,7 @@ import {
import { useNavigationElements } from "app/navigation/use-navigation-elements";
const Footer = () => {
+ const user = useContext(UserContext);
const router = useRouter();
const isDark = useIsDarkMode();
const isDarkThemePage = SWIPE_LIST_SCREENS.includes(router.pathname);
@@ -33,6 +36,11 @@ const Footer = () => {
const { width } = useWindowDimensions();
const { isTabBarHidden } = useNavigationElements();
+ const canCreateMusicDrop =
+ !!user?.user?.data.profile.bypass_track_ownership_validation ||
+ !!user?.user?.data.profile.spotify_artist_id ||
+ !!user?.user?.data.profile.apple_music_artist_id;
+
if (width >= 768) {
return null;
}
@@ -65,11 +73,13 @@ const Footer = () => {
color={color}
focused={router.pathname === "/channels"}
/>
-
+ {canCreateMusicDrop && (
+
+ )}
- {/* TODO: Creator Tokens P1
{isAuthenticated && (
{
@@ -218,7 +217,7 @@ function HeaderDropdown({
)}
- */}
+
{
)}
- {/* TODO: Creator Tokens P1
{isAuthenticated && (
{
@@ -484,7 +483,6 @@ export const HeaderMd = withColorScheme(() => {
)}
- */}
{
return (
<>
-
>
);
}
diff --git a/packages/app/components/home/popular-creators.tsx b/packages/app/components/home/popular-creators.tsx
deleted file mode 100644
index e2d4786696..0000000000
--- a/packages/app/components/home/popular-creators.tsx
+++ /dev/null
@@ -1,204 +0,0 @@
-import { memo, useCallback } from "react";
-import {
- useWindowDimensions,
- Dimensions,
- Platform,
- ListRenderItemInfo,
-} from "react-native";
-
-import { BorderlessButton } from "react-native-gesture-handler";
-
-import { Avatar } from "@showtime-xyz/universal.avatar";
-import { useIsDarkMode } from "@showtime-xyz/universal.hooks";
-import { CreatorChannel as CreatorChannelIcon } from "@showtime-xyz/universal.icon";
-import { Pressable } from "@showtime-xyz/universal.pressable";
-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 { View, ViewProps } from "@showtime-xyz/universal.view";
-
-import {
- useSuggestedChannelsList,
- CreatorChannel,
-} from "app/components/creator-channels/hooks/use-channels-list";
-import { DESKTOP_CONTENT_WIDTH } from "app/constants/layout";
-
-import { breakpoints } from "design-system/theme";
-
-import { useJoinChannel } from "../creator-channels/hooks/use-join-channel";
-import { HomeSlider } from "./home-slider";
-
-const PlatformPressable = Platform.OS === "web" ? Pressable : BorderlessButton;
-
-const INFO_HEIGTH = 230;
-const windowWidth = Dimensions.get("window").width;
-const PopularCreatorItem = memo(function PopularCreatorItem({
- item,
- width,
- style,
- tw = "",
- ...rest
-}: {
- item: CreatorChannel;
- width: number;
- index: number;
-} & ViewProps) {
- const isDark = useIsDarkMode();
- const router = useRouter();
- const joinChannel = useJoinChannel();
- const onJoinChannel = useCallback(async () => {
- await joinChannel.trigger({ channelId: item.id });
- router.push(`/channels/${item.id}?fresh=channel`);
- }, [item.id, joinChannel, router]);
-
- return (
- router.push(`/@${item.owner.username}`)}
- {...rest}
- >
-
-
-
-
-
-
-
-
- {item?.owner?.name || item?.owner?.username}
-
-
-
- {`${item?.member_count} members`}
-
-
-
- {item?.owner?.bio}
-
-
-
- Join
-
-
- );
-});
-const PopularCreatorSkeletonItem = ({
- width = 174,
- style,
- ...rest
-}: { width?: number } & ViewProps) => {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-export const PopularCreators = memo(function PopularCreators() {
- const { width } = useWindowDimensions();
- const isMdWidth = width >= breakpoints["md"];
- const { data, isLoading } = useSuggestedChannelsList({ pageSize: 10 });
- const isShowSeeAll = data.length > (isMdWidth ? 3 : 2);
- const router = useRouter();
- const pagerWidth = isMdWidth ? DESKTOP_CONTENT_WIDTH : windowWidth - 32;
- const itemWidth = Platform.select({
- web: undefined,
- default: pagerWidth / 2,
- });
- const renderItem = useCallback(
- ({ item, index }: ListRenderItemInfo) => (
-
- ),
- [itemWidth]
- );
- if (data.length === 0) return null;
- return (
-
-
-
- Popular artists
-
- {isShowSeeAll && (
- {
- router.push("/channels");
- }}
- shouldActivateOnStart
- hitSlop={10}
- >
-
- see all
-
-
- )}
-
-
- {isLoading ? (
-
-
-
-
-
-
- ) : (
-
- )}
-
-
- );
-});
diff --git a/packages/app/components/profile/creator-tokens-panel.tsx b/packages/app/components/profile/creator-tokens-panel.tsx
index e85fec60c0..70254be421 100644
--- a/packages/app/components/profile/creator-tokens-panel.tsx
+++ b/packages/app/components/profile/creator-tokens-panel.tsx
@@ -18,6 +18,7 @@ import { useWalletUSDCBalance } from "app/hooks/creator-token/use-wallet-usdc-ba
import { useWallet } from "app/hooks/use-wallet";
import { getCurrencyPrice } from "app/utilities";
+import { TextTooltip } from "../tooltips/text-tooltip";
import { PlatformBuyButton, PlatformSellButton } from "./buy-and-sell-buttons";
type CreatorTokensPanelProps = { isSelf?: boolean; username?: string };
@@ -157,8 +158,6 @@ export const CreatorTokensPanel = ({
username,
}: CreatorTokensPanelProps) => {
const isDark = useIsDarkMode();
- const router = useRouter();
- const { data: userProfileData } = useUserProfile({ address: username });
const usdcBalance = useWalletUSDCBalance();
@@ -171,84 +170,48 @@ export const CreatorTokensPanel = ({
- Wallet balance
+ USDC wallet balance
- {
- router.push(
- Platform.select({
- native: "/creator-token/explanation",
- web: {
- pathname: router.pathname,
- query: {
- ...router.query,
- creatorTokensExplanationModal: true,
- },
- } as any,
- }),
- Platform.select({
- native: "/creator-token/explanation",
- web:
- router.asPath === "/"
- ? "/creator-token/explanation"
- : router.asPath,
- }),
- { shallow: true }
- );
- }}
- hitSlop={{ top: 12, left: 12, right: 12, bottom: 12 }}
- >
-
-
+
+
+ }
+ text={
+ "Your estimated USDC wallet\nbalance on the Base network."
+ }
+ />
{getCurrencyPrice("USD", usdcBalance.data?.displayBalance)}
- {/*
- // TODO: Creator Tokens P1
+ {/* TODO: creator tokens p2
Token earnings
- {
- router.push(
- Platform.select({
- native: "/creator-token/explanation",
- web: {
- pathname: router.pathname,
- query: {
- ...router.query,
- creatorTokensExplanationModal: true,
- },
- } as any,
- }),
- Platform.select({
- native: "/creator-token/explanation",
- web:
- router.asPath === "/"
- ? "/creator-token/explanation"
- : router.asPath,
- }),
- { shallow: true }
- );
- }}
- hitSlop={{ top: 12, left: 12, right: 12, bottom: 12 }}
- >
-
-
+
+ }
+ text={
+ "Every time someone trades\nyour token you earn a 7%\nfee."
+ }
+ />
$21.67
diff --git a/packages/app/components/profile/import-allowlist.tsx b/packages/app/components/profile/import-allowlist.tsx
index 118082ac8e..6326fe5d8d 100644
--- a/packages/app/components/profile/import-allowlist.tsx
+++ b/packages/app/components/profile/import-allowlist.tsx
@@ -1,4 +1,4 @@
-import { useCallback } from "react";
+import { useCallback, useState } from "react";
import { Platform, Linking } from "react-native";
import axios from "axios";
@@ -22,6 +22,7 @@ import { toast } from "design-system/toast";
export const ImportAllowlist = () => {
const router = useRouter();
+ const [isLoading, setIsLoading] = useState(false);
const pickCSV = useCallback(async () => {
try {
const file = await DocumentPicker.getDocumentAsync({
@@ -38,30 +39,38 @@ export const ImportAllowlist = () => {
"Content-Type": `multipart/form-data`,
};
const attachment = file.assets[0].uri;
- if (Platform.OS === "web") {
- const attachmentFormData = await getFileFormData(attachment);
- const formData = new FormData();
- formData.append(
- "file",
- attachmentFormData!,
- generateRandomFilename(extractMimeType(attachment))
- );
- await axios({
- url: uploadUrl,
- method: "POST",
- headers: headers,
- data: formData,
- });
- } else {
- await FileSystem.uploadAsync(uploadUrl, attachment, {
- uploadType: FileSystem.FileSystemUploadType.MULTIPART,
- sessionType: FileSystem.FileSystemSessionType.BACKGROUND,
- fieldName: "file",
- httpMethod: "POST",
- headers,
- });
+
+ try {
+ setIsLoading(true);
+ if (Platform.OS === "web") {
+ const attachmentFormData = await getFileFormData(attachment);
+ const formData = new FormData();
+ formData.append(
+ "file",
+ attachmentFormData!,
+ generateRandomFilename(extractMimeType(attachment))
+ );
+ await axios({
+ url: uploadUrl,
+ method: "POST",
+ headers: headers,
+ data: formData,
+ });
+ } else {
+ await FileSystem.uploadAsync(uploadUrl, attachment, {
+ uploadType: FileSystem.FileSystemUploadType.MULTIPART,
+ sessionType: FileSystem.FileSystemSessionType.BACKGROUND,
+ fieldName: "file",
+ httpMethod: "POST",
+ headers,
+ });
+ }
+ toast.success("Allowlist imported successfully");
+ } catch {
+ toast.error("An error occurred while uploading the file");
+ } finally {
+ setIsLoading(false);
}
- toast.success("Allowlist imported successfully");
const nativeUrl = "/creator-token/import-allowlist-success";
const url = Platform.select({
@@ -107,14 +116,22 @@ export const ImportAllowlist = () => {
Creator Tokens to unlock your channel.
-