From 9f9e03079f675c2573c865694fe824974b447bac Mon Sep 17 00:00:00 2001 From: ikprk <168457495+ikprk@users.noreply.github.com> Date: Wed, 17 Jul 2024 13:54:59 +0200 Subject: [PATCH] :joy_cat: Categorized content on home view (#6414) * Adjust toggle button group * Rework homeview * Adjust config for testing * Bring back language filtering * Add new categories * Shuffle the categories and add analytics * Remove some categories after the testing * Revert "Adjust config for testing" This reverts commit 885464fccc9b6f339ab2665f5925f85f708d949a. --- .../ToggleButtonGroup/ToggleButtonGroup.tsx | 9 +- packages/atlas/src/config/contentFilter.ts | 4 +- .../atlas/src/hooks/useSegmentAnalytics.ts | 11 ++ packages/atlas/src/views/viewer/HomeView.tsx | 126 +++++++++++++++++- 4 files changed, 142 insertions(+), 8 deletions(-) diff --git a/packages/atlas/src/components/_inputs/ToggleButtonGroup/ToggleButtonGroup.tsx b/packages/atlas/src/components/_inputs/ToggleButtonGroup/ToggleButtonGroup.tsx index fde5632ddc..5cf3e4fa04 100644 --- a/packages/atlas/src/components/_inputs/ToggleButtonGroup/ToggleButtonGroup.tsx +++ b/packages/atlas/src/components/_inputs/ToggleButtonGroup/ToggleButtonGroup.tsx @@ -42,10 +42,13 @@ export type ToggleButtonFilterTypeProps = { filters: FilterButtonProps[] } & SharedToggleButtonProps -export type ToggleButtonGroupProps = ToggleButtonFilterTypeProps | ToggleButtonOptionTypeProps +export type ToggleButtonGroupProps = + | { + size?: 'small' | 'medium' | 'large' + } & (ToggleButtonFilterTypeProps | ToggleButtonOptionTypeProps) export function ToggleButtonGroup(props: ToggleButtonGroupProps) { - const { type, label, width = 'auto', className } = props + const { type, label, width = 'auto', className, size = 'small' } = props const optionWrapperRef = useRef(null) const { handleArrowScroll, handleMouseDown, isOverflow, visibleShadows } = useHorizonthalFade(optionWrapperRef) @@ -74,7 +77,7 @@ export function ToggleButtonGroup(props: ToggleButtonGroupProps) fullWidth variant={option.value !== props.value ? 'tertiary' : 'secondary'} onClick={() => props.onChange(option.value)} - size="small" + size={size} disabled={option.disabled} > {option.label} diff --git a/packages/atlas/src/config/contentFilter.ts b/packages/atlas/src/config/contentFilter.ts index c79f6b2d9c..498f121b3c 100644 --- a/packages/atlas/src/config/contentFilter.ts +++ b/packages/atlas/src/config/contentFilter.ts @@ -16,12 +16,12 @@ export const cancelledVideoFilter: VideoWhereInput = { }, } -// const browserLanguage = navigator.language?.split('-')[0] +const browserLanguage = navigator.language?.split('-')[0] export const singlePublicCryptoVideoFilter: VideoWhereInput = { isPublic_eq: true, isCensored_eq: false, - // orionLanguage_in: [...(browserLanguage ? [browserLanguage] : []), 'en'], + orionLanguage_in: [...(browserLanguage ? [browserLanguage] : []), 'en'], media: { isAccepted_eq: true, }, diff --git a/packages/atlas/src/hooks/useSegmentAnalytics.ts b/packages/atlas/src/hooks/useSegmentAnalytics.ts index 761e0433da..29efa30ae7 100644 --- a/packages/atlas/src/hooks/useSegmentAnalytics.ts +++ b/packages/atlas/src/hooks/useSegmentAnalytics.ts @@ -111,6 +111,16 @@ export const useSegmentAnalytics = () => { [analytics] ) + const trackHomepageCategorySelection = useCallback( + (categoryName: string) => { + analytics.track('Homepage Category Clicked', { + categoryName, + ...getUTMParams(), + }) + }, + [analytics, getUTMParams] + ) + const trackMembershipCreation = useCallback( (handle: string, email: string) => { analytics.track('Membership created', { @@ -703,5 +713,6 @@ export const useSegmentAnalytics = () => { trackRewardsReferralLinkClicked, trackRoundtableEventsClicked, trackRewardsCreateChannelButtonClick, + trackHomepageCategorySelection, } } diff --git a/packages/atlas/src/views/viewer/HomeView.tsx b/packages/atlas/src/views/viewer/HomeView.tsx index 7e542dc926..c197e1c644 100644 --- a/packages/atlas/src/views/viewer/HomeView.tsx +++ b/packages/atlas/src/views/viewer/HomeView.tsx @@ -1,5 +1,7 @@ import styled from '@emotion/styled' -import { FC } from 'react' +import { shuffle } from 'lodash-es' +import { FC, useCallback } from 'react' +import { useSearchParams } from 'react-router-dom' import { VideoOrderByInput } from '@/api/queries/__generated__/baseTypes.generated' import { @@ -7,25 +9,133 @@ import { useGetCuratedHompageVideosQuery, } from '@/api/queries/__generated__/videos.generated' import { Section } from '@/components/Section/Section' +import { ToggleButtonGroup } from '@/components/_inputs/ToggleButtonGroup' import { ReferralsBanner } from '@/components/_referrals/ReferralsBanner/ReferralsBanner' import { VideoContentTemplate } from '@/components/_templates/VideoContentTemplate' import { VideoTileViewer } from '@/components/_video/VideoTileViewer' +import { atlasConfig } from '@/config' import { getPublicCryptoVideoFilter } from '@/config/contentFilter' import { useHeadTags } from '@/hooks/useHeadTags' import { useInfiniteVideoGrid } from '@/hooks/useInfiniteVideoGrid' +import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' import { useVideoGridRows } from '@/hooks/useVideoGridRows' import { DEFAULT_VIDEO_GRID, sizes } from '@/styles' import { createPlaceholderData } from '@/utils/data' import { InfiniteLoadingOffsets } from '@/utils/loading.contants' +const _options = [ + { + label: 'Crypto', + configLabel: 'Crypto', + value: '5', + queryValue: atlasConfig.content.categories.find((category) => category.name === 'Crypto')?.videoCategories ?? [], + }, + { + label: 'Entertainment', + configLabel: 'Entertainment', + value: '8', + queryValue: + atlasConfig.content.categories.find((category) => category.name === 'Entertainment')?.videoCategories ?? [], + }, + { + label: 'Gaming', + configLabel: 'Video Games', + value: '20', + queryValue: + atlasConfig.content.categories.find((category) => category.name === 'Video Games')?.videoCategories ?? [], + }, + { + label: 'Music', + configLabel: 'Music and Music Videos', + value: '11', + queryValue: + atlasConfig.content.categories.find((category) => category.name === 'Music and Music Videos')?.videoCategories ?? + [], + }, + { + label: 'Blogs', + configLabel: 'People and Blogs', + value: '14', + queryValue: + atlasConfig.content.categories.find((category) => category.name === 'People and Blogs')?.videoCategories ?? [], + }, + { + label: 'Animation', + configLabel: 'Animation and Film', + value: '2', + queryValue: + atlasConfig.content.categories.find((category) => category.name === 'Animation and Film')?.videoCategories ?? [], + }, + { + label: 'Technology', + configLabel: 'Technology', + value: '17', + queryValue: + atlasConfig.content.categories.find((category) => category.name === 'Technology')?.videoCategories ?? [], + }, + { + label: 'Art', + configLabel: 'Art', + value: '1', + queryValue: atlasConfig.content.categories.find((category) => category.name === 'Art')?.videoCategories ?? [], + }, + { + label: 'Memes', + configLabel: 'Memes and Humour', + value: '10', + queryValue: + atlasConfig.content.categories.find((category) => category.name === 'Memes and Humour')?.videoCategories ?? [], + }, +] + +const options = [ + ...shuffle(_options), + { + label: 'Other', + value: 'other', + queryValue: + atlasConfig.content.categories + .filter( + (category) => + !_options.reduce((prev, next) => [...prev, next.configLabel], [] as string[]).includes(category.name) + ) + .map((cat) => cat.videoCategories) + .flat() ?? [], + }, +] + export const HomeView: FC = () => { const headTags = useHeadTags() - const { columns, fetchMore, tiles, loading, pageInfo } = useHomeVideos() + const [searchParams, setSearchParams] = useSearchParams() + const category = searchParams.get('category') ?? options[0].value + const { columns, fetchMore, tiles, loading, pageInfo } = useHomeVideos( + options.find((opt) => opt.value === category)?.queryValue + ) + const { trackHomepageCategorySelection } = useSegmentAnalytics() + + const setCategory = useCallback( + (value: string) => { + const categoryLabel = options.find((category) => category.value === value)?.label + if (categoryLabel) { + trackHomepageCategorySelection(categoryLabel) + } + + setSearchParams({ category: value }) + }, + [setSearchParams, trackHomepageCategorySelection] + ) return ( {headTags} + setCategory(newCategory as string)} + options={options} + /> { +const StyledToggleButtonGroup = styled(ToggleButtonGroup)` + margin-top: ${sizes(8)}; +` + +const useHomeVideos = (categories?: string[]) => { const initialRowsToLoad = useVideoGridRows('main') const { data, loading } = useGetCuratedHompageVideosQuery({ notifyOnNetworkStatusChange: true, @@ -62,6 +176,9 @@ const useHomeVideos = () => { where: getPublicCryptoVideoFilter({ orionLanguage_in: undefined, includeInHomeFeed_eq: true, + category: { + id_in: categories, + }, }), skipVideoIds: ['-1'], }, @@ -72,6 +189,9 @@ const useHomeVideos = () => { variables: { where: getPublicCryptoVideoFilter({ id_not_in: avoidIds, + category: { + id_in: categories, + }, }), orderBy: VideoOrderByInput.VideoRelevanceDesc, },