Ott 432 matches request (#138)

* refactor(#432): added saving callback in ref in intersection observer hook

* refactor(#432): removed filters from matches request

* refactor(#432): moved matches state from HomePage into Matches feature

* feat(#432): added infinite scroll grid matches to other profiles

Co-authored-by: mirlan.maksitaliev <mirlan.maksitaliev@instatsport.com>
keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
Mirlan 5 years ago committed by GitHub
parent 88e5dd9fb5
commit 73c0fbfe82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 76
      src/features/HomePage/hooks.tsx
  2. 14
      src/features/HomePage/index.tsx
  3. 8
      src/features/InfiniteScroll/hooks.tsx
  4. 39
      src/features/Matches/components/MatchesList/index.tsx
  5. 49
      src/features/Matches/helpers.tsx
  6. 143
      src/features/Matches/hooks.tsx
  7. 72
      src/features/Matches/index.tsx
  8. 43
      src/features/PlayerPage/hooks.tsx
  9. 4
      src/features/PlayerPage/index.tsx
  10. 38
      src/features/TeamPage/hooks.tsx
  11. 4
      src/features/TeamPage/index.tsx
  12. 38
      src/features/TournamentPage/hooks.tsx
  13. 4
      src/features/TournamentPage/index.tsx
  14. 4
      src/requests/getMatches.tsx

@ -1,17 +1,6 @@
import {
useCallback,
useEffect,
useState,
useRef,
} from 'react'
import type { Matches } from 'requests'
import { getMatches } from 'requests'
import { useMemo } from 'react'
import { useHeaderFiltersStore } from 'features/HeaderFilters'
import { useRequest } from 'hooks'
const MATCHES_LIMIT = 60
export const useHomePage = () => {
const {
@ -21,66 +10,17 @@ export const useHomePage = () => {
selectedTournamentId,
} = useHeaderFiltersStore()
const {
isFetching,
request: requestMatches,
} = useRequest(getMatches)
const [matches, setMatches] = useState<Matches>({
broadcast: [],
features: [],
hasNextPage: true,
highlights: [],
isVideoSections: false,
})
const pageRef = useRef(0)
const fetchMatches = useCallback((page: number) => (
requestMatches({
date: selectedDateFormatted,
limit: MATCHES_LIMIT,
matchStatus: selectedMatchStatus,
offset: page * MATCHES_LIMIT,
sportType: selectedSportTypeId,
tournamentId: selectedTournamentId,
})
), [
const requestArgs = useMemo(() => ({
date: selectedDateFormatted,
matchStatus: selectedMatchStatus,
sportType: selectedSportTypeId,
tournamentId: selectedTournamentId,
}), [
selectedDateFormatted,
selectedMatchStatus,
selectedSportTypeId,
selectedTournamentId,
requestMatches,
])
const { hasNextPage } = matches
const fetchMoreMatches = useCallback(async () => {
if (!hasNextPage || isFetching) return
const newMatches = await fetchMatches(pageRef.current)
setMatches((oldMatches): Matches => {
const broadcast = [...oldMatches.broadcast, ...newMatches.broadcast]
return {
...oldMatches,
broadcast,
hasNextPage: newMatches.hasNextPage,
}
})
pageRef.current += 1
}, [
fetchMatches,
hasNextPage,
isFetching,
])
useEffect(() => {
fetchMatches(0).then(setMatches)
pageRef.current = 1
}, [fetchMatches])
return {
fetchMoreMatches,
isFetching,
matches,
}
return { requestArgs }
}

@ -1,23 +1,15 @@
import React from 'react'
import { InfiniteScroll } from 'features/InfiniteScroll'
import { Matches } from 'features/Matches'
import { useHomePage } from './hooks'
import { Content, Loading } from './styled'
import { Content } from './styled'
export const HomePage = () => {
const {
fetchMoreMatches,
isFetching,
matches,
} = useHomePage()
const { requestArgs } = useHomePage()
return (
<Content>
<InfiniteScroll onFetchMore={fetchMoreMatches}>
<Matches matches={matches} />
</InfiniteScroll>
{isFetching && <Loading>Loading...</Loading>}
<Matches requestArgs={requestArgs} />
</Content>
)
}

@ -16,11 +16,16 @@ type Args = {
* пересечение с областью видимости документа
*/
export const useIntersectionObserver = ({ onIntersect, options }: Args) => {
const callbackRef = useRef(onIntersect)
const rootRef = useRef<HTMLDivElement>(null)
const targetRef = useRef<HTMLDivElement>(null)
const rootElement = rootRef.current
const targetElement = targetRef.current
useEffect(() => {
callbackRef.current = onIntersect
}, [onIntersect])
useEffect(() => {
if (!targetElement) {
return undefined
@ -29,7 +34,7 @@ export const useIntersectionObserver = ({ onIntersect, options }: Args) => {
const observerOptions = { root: rootElement, ...options }
const callback = ([target]: Array<IntersectionObserverEntry>) => {
if (target.isIntersecting) {
onIntersect(target, observer)
callbackRef.current(target, observer)
}
}
@ -42,7 +47,6 @@ export const useIntersectionObserver = ({ onIntersect, options }: Args) => {
}, [
rootElement,
targetElement,
onIntersect,
options,
])

@ -0,0 +1,39 @@
import React, { Fragment } from 'react'
import isEmpty from 'lodash/isEmpty'
import type { Match } from 'features/Matches/hooks'
import { MatchesSlider } from 'features/MatchesSlider'
import { MatchesGrid } from 'features/MatchesGrid'
import { T9n } from 'features/T9n'
import { Title, Section } from '../../styled'
const matchesComponents = {
grid: MatchesGrid,
slider: MatchesSlider,
}
type Props = {
as: keyof typeof matchesComponents,
matches: Array<Match>,
title: string,
}
export const MatchesList = ({
as,
matches,
title,
}: Props) => {
const Component = matchesComponents[as]
return (
<Fragment>
{!isEmpty(matches) && (
<Section>
<Title><T9n t={title} /></Title>
<Component matches={matches} />
</Section>
)}
</Fragment>
)
}

@ -0,0 +1,49 @@
import map from 'lodash/map'
import flatten from 'lodash/flatten'
import pipe from 'lodash/fp/pipe'
import fpMap from 'lodash/fp/map'
import type { Content } from 'requests'
import { ProfileTypes } from 'config'
import { getProfileLogo, getSportLexic } from 'helpers'
type Name = 'name_rus' | 'name_eng'
const prepareMatch = ({
matches: matchesList,
sport,
...rest
}: Content, suffix: string) => map(matchesList, ({
date,
id,
stream_status,
team1,
team2,
}) => ({
date,
id,
preview: '/images/preview.png',
sportName: getSportLexic(sport),
sportType: sport,
streamStatus: stream_status,
team1Logo: getProfileLogo({
id: team1.id,
profileType: ProfileTypes.TEAMS,
sportType: sport,
}),
team1Name: team1[`name_${suffix}` as Name],
team1Score: team1.score,
team2Logo: getProfileLogo({
id: team2.id,
profileType: ProfileTypes.TEAMS,
sportType: sport,
}),
team2Name: team2[`name_${suffix}` as Name],
team2Score: team2.score,
tournamentName: rest[`name_${suffix}` as Name],
}))
export const prepareMatches = (content: Array<Content>, suffix: string) => pipe(
fpMap((items: Content) => prepareMatch(items, suffix)),
flatten,
)(content)

@ -1,82 +1,93 @@
import { useMemo } from 'react'
import {
useCallback,
useEffect,
useState,
useMemo,
useRef,
} from 'react'
import map from 'lodash/map'
import flatten from 'lodash/flatten'
import pipe from 'lodash/fp/pipe'
import fpMap from 'lodash/fp/map'
import type { Matches } from 'requests'
import { getMatches } from 'requests'
import { useRequest } from 'hooks'
import type { Matches, Content } from 'requests'
import { ProfileTypes } from 'config'
import { getProfileLogo, getSportLexic } from 'helpers'
import { useLexicsStore } from 'features/LexicsStore'
import { MatchStatuses } from 'features/HeaderFilters'
import { prepareMatches } from './helpers'
export type Match = ReturnType<typeof prepareMatches>[number]
type RequestArgs = Omit<Parameters<typeof getMatches>[0], 'limit' | 'offset'>
export type Props = {
matches: Matches,
requestArgs: RequestArgs,
}
type Name = 'name_rus' | 'name_eng'
export type Match = {
date: string,
id: number,
preview: string,
sportName: string,
sportType: number,
streamStatus: MatchStatuses,
team1Logo: string,
team1Name: string,
team1Score: number,
team2Logo: string,
team2Name: string,
team2Score: number,
tournamentName: string,
}
const MATCHES_LIMIT = 60
const prepareMatches = (content: Array<Content>, suffix: string) => pipe(
fpMap<Content, Array<Match>>(({
matches: matchesList,
sport,
...rest
}) => map(matchesList, ({
date,
id,
stream_status,
team1,
team2,
}) => ({
date,
id,
preview: '/images/preview.png',
sportName: getSportLexic(sport),
sportType: sport,
streamStatus: stream_status,
team1Logo: getProfileLogo({
id: team1.id,
profileType: ProfileTypes.TEAMS,
sportType: sport,
}),
team1Name: team1[`name_${suffix}` as Name],
team1Score: team1.score,
team2Logo: getProfileLogo({
id: team2.id,
profileType: ProfileTypes.TEAMS,
sportType: sport,
}),
team2Name: team2[`name_${suffix}` as Name],
team2Score: team2.score,
tournamentName: rest[`name_${suffix}` as Name],
}))),
flatten,
)(content) as Array<Match>
export const useMatches = ({ matches }: Props) => {
export const useMatches = ({ requestArgs }: Props) => {
const { suffix } = useLexicsStore()
const {
isFetching,
request: requestMatches,
} = useRequest(getMatches)
const pageRef = useRef(0)
const [matches, setMatches] = useState<Matches>({
broadcast: [],
features: [],
hasNextPage: true,
highlights: [],
isVideoSections: false,
})
return useMemo(() => ({
const { hasNextPage } = matches
const fetchMatches = useCallback((page: number) => (
requestMatches({
...requestArgs,
limit: MATCHES_LIMIT,
offset: page * MATCHES_LIMIT,
})
), [
requestMatches,
requestArgs,
])
const fetchMoreMatches = useCallback(async () => {
if (!hasNextPage || isFetching) return
const newMatches = await fetchMatches(pageRef.current)
setMatches((oldMatches): Matches => {
const broadcast = [...oldMatches.broadcast, ...newMatches.broadcast]
return {
...oldMatches,
broadcast,
hasNextPage: newMatches.hasNextPage,
}
})
pageRef.current += 1
}, [
fetchMatches,
hasNextPage,
isFetching,
])
useEffect(() => {
fetchMatches(0).then(setMatches)
pageRef.current = 1
}, [fetchMatches])
const preparedMatches = useMemo(() => ({
broadcast: prepareMatches(matches.broadcast, suffix),
features: prepareMatches(matches.features, suffix),
highlights: prepareMatches(matches.highlights, suffix),
isVideoSections: matches.isVideoSections,
}), [suffix, matches])
return {
fetchMoreMatches,
isFetching,
matches: preparedMatches,
}
}

@ -1,60 +1,56 @@
import React, { Fragment } from 'react'
import isEmpty from 'lodash/isEmpty'
import { MatchesSlider } from 'features/MatchesSlider'
import { MatchesGrid } from 'features/MatchesGrid'
import { T9n } from 'features/T9n'
import { InfiniteScroll } from 'features/InfiniteScroll'
import { Loading } from 'features/HomePage/styled'
import type { Props } from './hooks'
import { useMatches } from './hooks'
import { Title, Section } from './styled'
import { MatchesList } from './components/MatchesList'
export type { Match } from './hooks'
export const Matches = (props: Props) => {
const {
broadcast,
features,
highlights,
isVideoSections,
fetchMoreMatches,
isFetching,
matches: {
broadcast,
features,
highlights,
isVideoSections,
},
} = useMatches(props)
if (isVideoSections) {
return (
<Fragment>
{!isEmpty(broadcast) && (
<Section>
<Title><T9n t='broadcast' /></Title>
<MatchesSlider matches={broadcast} />
</Section>
)}
{!isEmpty(highlights) && (
<Section>
<Title><T9n t='round_highilights' /></Title>
<MatchesSlider matches={highlights} />
</Section>
)}
{!isEmpty(features) && (
<Section>
<Title><T9n t='features' /></Title>
<MatchesSlider matches={highlights} />
</Section>
)}
<MatchesList
as='slider'
title='broadcast'
matches={broadcast}
/>
<MatchesList
as='slider'
title='round_highilights'
matches={highlights}
/>
<MatchesList
as='slider'
title='features'
matches={features}
/>
</Fragment>
)
}
return (
<Fragment>
{!isEmpty(broadcast) && (
<Section>
<Title><T9n t='broadcast' /></Title>
<MatchesGrid matches={broadcast} />
</Section>
)}
</Fragment>
<InfiniteScroll onFetchMore={fetchMoreMatches}>
<MatchesList
as='grid'
title='broadcast'
matches={broadcast}
/>
{isFetching && <Loading>Loading...</Loading>}
</InfiniteScroll>
)
}

@ -1,14 +1,15 @@
import { useEffect, useState } from 'react'
import {
useEffect,
useState,
useMemo,
} from 'react'
import { useSportNameParam, usePageId } from 'hooks'
import type { Matches } from 'requests'
import { getMatches } from 'requests'
import type { PlayerProfile } from 'requests/getPlayerInfo'
import { getPlayerInfo } from 'requests/getPlayerInfo'
import { useLexicsStore } from 'features/LexicsStore'
import { useHeaderFiltersStore } from 'features/HeaderFilters'
type Firstname = 'firstname_eng' | 'firstname_rus'
type Lastname = 'lastname_eng' | 'lastname_rus'
@ -17,12 +18,8 @@ type Name = 'name_eng' | 'name_rus'
export const usePlayerPage = () => {
const [playerProfile, setPlayerProfile] = useState<PlayerProfile>(null)
const { sportType } = useSportNameParam()
const pageId = usePageId()
const playerId = usePageId()
const { suffix, translate } = useLexicsStore()
const {
selectedDateFormatted,
selectedMatchStatus,
} = useHeaderFiltersStore()
const {
club_team,
@ -42,33 +39,17 @@ export const usePlayerPage = () => {
]
useEffect(() => {
getPlayerInfo(pageId, sportType).then(setPlayerProfile)
}, [pageId, sportType])
const [matches, setMatches] = useState<Matches>({
broadcast: [],
features: [],
highlights: [],
isVideoSections: false,
})
getPlayerInfo(playerId, sportType).then(setPlayerProfile)
}, [playerId, sportType])
useEffect(() => {
getMatches({
date: selectedDateFormatted,
matchStatus: selectedMatchStatus,
playerId: pageId,
sportType,
}).then(setMatches)
}, [
pageId,
selectedDateFormatted,
selectedMatchStatus,
const requestArgs = useMemo(() => ({
playerId,
sportType,
])
}), [playerId, sportType])
return {
infoItems,
matches,
name: fullName,
requestArgs,
}
}

@ -9,8 +9,8 @@ import { usePlayerPage } from './hooks'
export const PlayerPage = () => {
const {
infoItems,
matches,
name,
requestArgs,
} = usePlayerPage()
return (
@ -20,7 +20,7 @@ export const PlayerPage = () => {
name={name}
infoItems={infoItems}
/>
<Matches matches={matches} />
<Matches requestArgs={requestArgs} />
</Fragment>
)
}

@ -1,9 +1,12 @@
import { useEffect, useState } from 'react'
import {
useEffect,
useState,
useMemo,
} from 'react'
import type { TeamInfo, Matches } from 'requests'
import { getTeamInfo, getMatches } from 'requests'
import type { TeamInfo } from 'requests'
import { getTeamInfo } from 'requests'
import { useHeaderFiltersStore } from 'features/HeaderFilters'
import { useLexicsStore } from 'features/LexicsStore'
import { useSportNameParam, usePageId } from 'hooks'
@ -16,18 +19,6 @@ export const useTeamPage = () => {
const teamId = usePageId()
const { suffix } = useLexicsStore()
const {
selectedDateFormatted,
selectedMatchStatus,
} = useHeaderFiltersStore()
const [matches, setMatches] = useState<Matches>({
broadcast: [],
features: [],
highlights: [],
isVideoSections: false,
})
const {
[`name_${suffix}` as Name]: name = '',
} = teamProfile || {}
@ -45,24 +36,15 @@ export const useTeamPage = () => {
teamId,
])
useEffect(() => {
getMatches({
date: selectedDateFormatted,
matchStatus: selectedMatchStatus,
sportType,
teamId,
}).then(setMatches)
}, [
selectedDateFormatted,
selectedMatchStatus,
const requestArgs = useMemo(() => ({
sportType,
teamId,
])
}), [teamId, sportType])
return {
infoItems: [country],
matches,
name,
requestArgs,
sportType,
}
}

@ -11,8 +11,8 @@ import { Content } from './styled'
export const TeamPage = () => {
const {
infoItems,
matches,
name,
requestArgs,
} = useTeamPage()
return (
@ -23,7 +23,7 @@ export const TeamPage = () => {
name={name}
infoItems={infoItems}
/>
<Matches matches={matches} />
<Matches requestArgs={requestArgs} />
</Content>
</Fragment>
)

@ -1,10 +1,13 @@
import { useEffect, useState } from 'react'
import {
useEffect,
useState,
useMemo,
} from 'react'
import { useHeaderFiltersStore } from 'features/HeaderFilters'
import { useLexicsStore } from 'features/LexicsStore'
import type { TournamentInfo, Matches } from 'requests'
import { getTournamentInfo, getMatches } from 'requests'
import type { TournamentInfo } from 'requests'
import { getTournamentInfo } from 'requests'
import { useSportNameParam, usePageId } from 'hooks'
@ -16,18 +19,6 @@ export const useTournamentPage = () => {
const tournamentId = usePageId()
const { suffix } = useLexicsStore()
const {
selectedDateFormatted,
selectedMatchStatus,
} = useHeaderFiltersStore()
const [matches, setMatches] = useState<Matches>({
broadcast: [],
features: [],
highlights: [],
isVideoSections: false,
})
const {
[`name_${suffix}` as Name]: name = '',
} = tournamentProfile || {}
@ -45,23 +36,14 @@ export const useTournamentPage = () => {
tournamentId,
])
useEffect(() => {
getMatches({
date: selectedDateFormatted,
matchStatus: selectedMatchStatus,
sportType,
tournamentId,
}).then(setMatches)
}, [
selectedDateFormatted,
selectedMatchStatus,
const requestArgs = useMemo(() => ({
sportType,
tournamentId,
])
}), [tournamentId, sportType])
return {
infoItems: [country],
matches,
name,
requestArgs,
}
}

@ -11,8 +11,8 @@ import { Content } from './styled'
export const TournamentPage = () => {
const {
infoItems,
matches,
name,
requestArgs,
} = useTournamentPage()
return (
@ -23,7 +23,7 @@ export const TournamentPage = () => {
name={name}
infoItems={infoItems}
/>
<Matches matches={matches} />
<Matches requestArgs={requestArgs} />
</Content>
</Fragment>
)

@ -51,9 +51,9 @@ type Team = {
}
type Args = {
date: string,
date?: string,
limit?: number,
matchStatus: MatchStatuses | null,
matchStatus?: MatchStatuses | null,
offset?: number,
playerId?: number | null,
sportType: SportTypes | null,

Loading…
Cancel
Save