diff --git a/public/images/landing_1.png b/public/images/landing_1.png new file mode 100644 index 00000000..157d5bfe Binary files /dev/null and b/public/images/landing_1.png differ diff --git a/public/images/landing_2.png b/public/images/landing_2.png new file mode 100644 index 00000000..00973b97 Binary files /dev/null and b/public/images/landing_2.png differ diff --git a/public/images/landing_3.png b/public/images/landing_3.png new file mode 100644 index 00000000..749f9939 Binary files /dev/null and b/public/images/landing_3.png differ diff --git a/public/index.html b/public/index.html index 7a717257..05435e1a 100644 --- a/public/index.html +++ b/public/index.html @@ -11,13 +11,13 @@ content="width=device-width, initial-scale=1.0, maximum-scale=1 user-scalable=0" /> - @@ -27,5 +27,14 @@
+ + + diff --git a/src/config/lexics/indexLexics.tsx b/src/config/lexics/indexLexics.tsx index 3f53847a..77c78e71 100644 --- a/src/config/lexics/indexLexics.tsx +++ b/src/config/lexics/indexLexics.tsx @@ -6,11 +6,15 @@ const matchPopupLexics = { apply: 13491, episode_duration: 13410, events: 1020, + from_end_match: 15396, + from_start_match: 15395, gk: 3515, go_back_to_match: 13405, + half_time: 1033, languages: 15030, match_interviews: 13031, match_settings: 13490, + no_data: 15397, players_episodes: 13398, playlist_format: 13406, playlist_format_all_actions: 13408, diff --git a/src/config/lexics/joinMatch.tsx b/src/config/lexics/joinMatch.tsx new file mode 100644 index 00000000..728c9763 --- /dev/null +++ b/src/config/lexics/joinMatch.tsx @@ -0,0 +1,6 @@ +export const joinMatchLexics = { + join_instat_tv: 15420, + join_now: 15422, + promo_text: 15421, + watch_live: 15423, +} diff --git a/src/config/procedures.tsx b/src/config/procedures.tsx index ebb67641..f6e2c165 100644 --- a/src/config/procedures.tsx +++ b/src/config/procedures.tsx @@ -17,7 +17,9 @@ export const PROCEDURES = { get_user_info: 'get_user_info', get_user_match_second: 'get_user_match_second', get_user_subscriptions: 'get_user_subscriptions', + landing_get_match_info: 'landing_get_match_info', lst_c_country: 'lst_c_country', + ott_match_events: 'ott_match_events', ott_match_popup: 'ott_match_popup', ott_match_popup_actions: 'ott_match_popup_actions', ott_match_popup_player_playlist: 'ott_match_popup_player_playlist', diff --git a/src/features/AddCardForm/components/Form/hooks/index.tsx b/src/features/AddCardForm/components/Form/hooks/index.tsx index 9e91b323..938edb60 100644 --- a/src/features/AddCardForm/components/Form/hooks/index.tsx +++ b/src/features/AddCardForm/components/Form/hooks/index.tsx @@ -130,7 +130,7 @@ export const useFormSubmit = ({ onAddSuccess }: Props) => { setLoader(true) onAddCard(token.id) .then(onAddSuccess) - .then(() => setLoader(false)) + .finally(() => setLoader(false)) } } diff --git a/src/features/AddCardForm/styled.tsx b/src/features/AddCardForm/styled.tsx index b37d0f16..34e2ccc4 100644 --- a/src/features/AddCardForm/styled.tsx +++ b/src/features/AddCardForm/styled.tsx @@ -13,7 +13,7 @@ export const Column = styled.div` export const ButtonsBlock = styled.div` display: flex; flex-direction: column; - align-items: center; + align-items: start; margin-top: 25px; ` diff --git a/src/features/App/index.tsx b/src/features/App/index.tsx index 5bc7d023..2b8f6c59 100644 --- a/src/features/App/index.tsx +++ b/src/features/App/index.tsx @@ -8,12 +8,16 @@ import { useAuthStore } from 'features/AuthStore' import { Background } from 'features/Background' import { GlobalStyles } from 'features/GlobalStyles' import { Theme } from 'features/Theme' +import { JoinMatchPage } from 'features/JoinMatchPage' import { AuthenticatedApp } from './AuthenticatedApp' +import { isMatchPage } from '../JoinMatchPage/helpers' const Main = () => { const { loadingUser, user } = useAuthStore() + if (!user && isMatchPage()) return + // юзер считывается из localstorage или // access_token токен истек и запрашивается новый if (loadingUser || user?.expired) return null diff --git a/src/features/ArrowLoader/styled.tsx b/src/features/ArrowLoader/styled.tsx index 9f960bb5..b9f617ab 100644 --- a/src/features/ArrowLoader/styled.tsx +++ b/src/features/ArrowLoader/styled.tsx @@ -17,7 +17,7 @@ export const Wrapper = styled.button` box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3); border-color: transparent; width: ${({ width = 'auto' }) => width}; - height: 50px; + height: 2.4rem; background-color: ${({ backgroundColor = '#294fc4' }) => backgroundColor}; border-radius: 5px; display: flex; diff --git a/src/features/AuthStore/hooks/useAuth.tsx b/src/features/AuthStore/hooks/useAuth.tsx index 5ba4fc43..06298acf 100644 --- a/src/features/AuthStore/hooks/useAuth.tsx +++ b/src/features/AuthStore/hooks/useAuth.tsx @@ -17,6 +17,7 @@ import { setCookie, removeCookie } from 'helpers/cookie' import { useToggle } from 'hooks' import { queryParamStorage } from 'features/QueryParamsStorage' +import { isMatchPage } from 'features/JoinMatchPage/helpers' import { getClientSettings } from '../helpers' @@ -83,8 +84,9 @@ export const useAuth = () => { if (isRedirectedBackFromAuthProvider) { signinRedirectCallback() } else { - // обычная загрузка страницы, проверяем пользователя в localstorage - checkUser().catch(login) + checkUser().catch(() => { + if (!isMatchPage()) login() + }) } }, [ checkUser, @@ -117,11 +119,13 @@ export const useAuth = () => { const auth = useMemo(() => ({ loadingUser, + login, logout, user, }), [ logout, user, + login, loadingUser, ]) diff --git a/src/features/JoinMatchPage/helpers.tsx b/src/features/JoinMatchPage/helpers.tsx new file mode 100644 index 00000000..3a8729cc --- /dev/null +++ b/src/features/JoinMatchPage/helpers.tsx @@ -0,0 +1,10 @@ +import toNumber from 'lodash/toNumber' +import isUndefined from 'lodash/isUndefined' + +export const isMatchPage = () => { + const splitPath = window.location.pathname.split('/') + const pageType = splitPath[2] + const matchId = toNumber(splitPath[3]) + + return pageType === 'matches' && !isUndefined(matchId) +} diff --git a/src/features/JoinMatchPage/hooks.tsx b/src/features/JoinMatchPage/hooks.tsx new file mode 100644 index 00000000..2a2ca644 --- /dev/null +++ b/src/features/JoinMatchPage/hooks.tsx @@ -0,0 +1,48 @@ +import type { FormEvent } from 'react' +import { + useEffect, + useState, +} from 'react' + +import format from 'date-fns/format' + +import type { UnauthenticatedMatch } from 'requests' +import { getUnauthenticatedMatch } from 'requests' + +import { usePageParams } from 'hooks/usePageParams' + +import { useLexicsStore } from 'features/LexicsStore' +import { getName } from 'features/Name' +import { useAuthStore } from 'features/AuthStore' + +export const useUnauthenticatedMatch = () => { + const [ + matchInfo, setMatchInfo, + ] = useState(null) + + const { suffix } = useLexicsStore() + const { profileId: matchId, sportType } = usePageParams() + + const teamName1 = getName({ nameObj: matchInfo?.team1 || {}, suffix }) + const teamName2 = getName({ nameObj: matchInfo?.team2 || {}, suffix }) + + const date = matchInfo?.date + const matchDate = (date && format(new Date(date), 'd MMM HH:mm')) || '' + + const { login } = useAuthStore() + const handleSubmit = (event: FormEvent) => { + event.preventDefault() + login() + } + + useEffect(() => { + getUnauthenticatedMatch(sportType, matchId).then(setMatchInfo) + }, [sportType, matchId]) + + return { + handleSubmit, + matchDate, + teamName1, + teamName2, + } +} diff --git a/src/features/JoinMatchPage/index.tsx b/src/features/JoinMatchPage/index.tsx new file mode 100644 index 00000000..085e1b1e --- /dev/null +++ b/src/features/JoinMatchPage/index.tsx @@ -0,0 +1,90 @@ +import format from 'date-fns/format' + +import { joinMatchLexics } from 'config/lexics/joinMatch' + +import { usePageParams } from 'hooks/usePageParams' + +import { T9n } from 'features/T9n' +import { useLexicsConfig } from 'features/LexicsStore' + +import { useUnauthenticatedMatch } from './hooks' + +import { + Wrapper, + MainLogo, + HeaderWrapper, + Footer, + BlockWrapper, + MatchInfo, + SportImgWrapper, + DateInfoWrapper, + DateInfo, + WatchLive, + TeamsNameWrapper, + MainInfoTitle, + MainInfoButton, + MainInfoText, + EmptySpan, + WatchLiveCircle, + WatchLiveText, + SportImgMobileDevice, + FooterLogo, + FooterRights, +} from './styled' + +export const JoinMatchPage = () => { + useLexicsConfig(joinMatchLexics) + + const { sportType } = usePageParams() + const { + handleSubmit, + matchDate, + teamName1, + teamName2, + } = useUnauthenticatedMatch() + + const currentYear = format(new Date(), 'Y') + + return ( + + + + + + + + + {matchDate} + + + + + + + + + {teamName1} + + {teamName2} + + + + + + + + +
+ + + +
+
+
+
+ + ©InStat TV {currentYear} +
+
+ ) +} diff --git a/src/features/JoinMatchPage/styled.tsx b/src/features/JoinMatchPage/styled.tsx new file mode 100644 index 00000000..12388125 --- /dev/null +++ b/src/features/JoinMatchPage/styled.tsx @@ -0,0 +1,258 @@ +import styled, { css } from 'styled-components/macro' + +import { devices, SportTypes } from 'config' +import { isMobileDevice } from 'config/userAgent' + +import { ButtonSolid } from 'features/Common' +import { Logo } from 'features/Logo' + +type Props = { + sportType: SportTypes, +} + +export const Wrapper = styled.div` + width: 100vw; + height: 100vh; + color: white; + display: flex; + flex-direction: column; + justify-content: space-between; +` + +export const HeaderWrapper = styled.div` + background-color: #13151B; + padding: 20px 0; + padding-left: 20%; + width: 100%; + @media ${devices.laptop} { + padding-left: 5%; + } + ${isMobileDevice + ? css` + display: none; + ` + : ''}; +` + +export const MainLogo = styled(Logo)` + height: 20px; + width: 80px; +` + +export const BlockWrapper = styled.div` + height: 100%; + display: flex; + align-items: center; + padding-left: 20%; + width: 100%; + @media ${devices.laptop} { + padding-left: 5%; + } + @media ${devices.mobile} { + padding-left: 5.4rem; + } + ${isMobileDevice + ? css` + padding: 5.4rem; + overflow: scroll; + @media screen and (orientation: landscape){ + padding-top: 20px; + height: auto; + } + ` + : ''}; +` + +export const SportImgWrapper = styled.div` + background-image: url(/images/landing_${({ sportType }) => sportType}.png); + background-repeat: no-repeat; + background-position: center; + background-size: contain; + width: 512px; + height: 641px; + margin-right: 5%; + ${isMobileDevice + ? css` + display: none; + @media screen and (orientation: landscape){ + display: block; + height: 100%; + width: 70%; + } + ` + : ''}; +` + +export const MatchInfo = styled.div` + ${isMobileDevice + ? css` + width: 100%; + height: 100%; + @media screen and (orientation: landscape){ + padding-top: 0; + } + ` + : ''}; +` + +export const DateInfoWrapper = styled.div` + display: flex; + align-items: center; + font-size: 15px; + font-weight: 600; + ${isMobileDevice + ? css` + justify-content: space-between; + font-size: 14px; + ` + : ''}; +` + +export const DateInfo = styled.div` + text-transform: uppercase; + background-color: rgba(0,0,0,0.4); + padding: 8px 25px; + color: #B9B9B9; + margin-right: 25px; + border-radius: 5px; + ${isMobileDevice + ? css` + padding: 0.7em 2.5rem; + ` + : ''}; +` + +export const WatchLive = styled.div` + display: flex; + align-items: center; +` + +export const WatchLiveText = styled.div` + color: #EB5757; + letter-spacing: .2rem; +` + +export const WatchLiveCircle = styled.div` + width: 20px; + height: 20px; + border-radius: 50%; + background-color: #CC0000; + margin-right: 5px; + ${isMobileDevice + ? css` + width: 15px; + height: 15px; + ` + : ''}; +` + +export const TeamsNameWrapper = styled.div` + margin: 35px 0 40px; + font-size: 20px; + font-weight: 500; + ${isMobileDevice + ? css` + margin: 10px 0; + font-size: 16px; + ` + : ''}; +` + +export const MainInfoTitle = styled.div` + font-weight: 600; + font-size: 46px; + ${isMobileDevice + ? css` + font-size: 32px; + margin-bottom: 15px; + ` + : ''}; +` + +export const MainInfoButton = styled(ButtonSolid)` + width: 260px; + height: 50px; + font-size: 20px; + ${isMobileDevice + ? css` + width: 100%; + border-radius: 10px; + font-size: 17px; + margin-bottom: 30px; + ` + : ''}; +` + +export const MainInfoText = styled.div` + max-width: 400px; + margin: 40px 0; + font-size: 17px; + line-height: 150%; + ${isMobileDevice + ? css` + font-size: 15px; + margin: 15px 0 25px; + width: 85%; + ` + : ''}; +` + +export const Footer = styled.div` + font-size: 14px; + background-color: black; + padding: 16px 0; + padding-left: 20%; + width: 100%; + @media ${devices.laptop} { + padding-left: 5%; + } + @media ${devices.mobile} { + padding-left: 35px; + } + ${isMobileDevice + ? css` + display: flex; + padding: 15px 35px; + justify-content: space-between; + align-items: center; + ` + : ''}; +` + +export const FooterLogo = styled(Logo)` + display: none; + ${isMobileDevice + ? css` + display: block; + width: 48px; + height: 11px; + opacity: .4; + ` + : ''}; +` + +export const FooterRights = styled.div` + opacity: .5; + ${isMobileDevice + ? css` + font-size: 12px; + ` + : ''}; +` + +export const SportImgMobileDevice = styled(SportImgWrapper)` + display: none; + ${isMobileDevice + ? css` + display: block; + height: 53%; + width: 100%; + margin-right: 0; + @media screen and (orientation: landscape){ + display: none; + } + ` + : ''}; +` + +export const EmptySpan = styled.span`` diff --git a/src/features/MatchPage/components/FinishedMatch/hooks/index.tsx b/src/features/MatchPage/components/FinishedMatch/hooks/index.tsx index 5ce9849f..41992140 100644 --- a/src/features/MatchPage/components/FinishedMatch/hooks/index.tsx +++ b/src/features/MatchPage/components/FinishedMatch/hooks/index.tsx @@ -13,6 +13,7 @@ import { useMatchPopupStore } from 'features/MatchPopup' import { usePlayerLogger } from './usePlayerLogger' import { useEpisodes } from './useEpisodes' +import { useEvents } from './useEvents' import { useChapters } from './useChapters' export type Props = { @@ -37,6 +38,7 @@ export const useFinishedMatch = ({ profile }: Props) => { } = useToggle() const { episodes } = useEpisodes() + const { events } = useEvents() const { logPlaylistChange, onPlayingChange } = usePlayerLogger() @@ -75,6 +77,7 @@ export const useFinishedMatch = ({ profile }: Props) => { return { closeSettingsPopup, + events, isSettingsPopupOpen, onPlayingChange, onPlaylistSelect, diff --git a/src/features/MatchPage/components/FinishedMatch/hooks/useEpisodes.tsx b/src/features/MatchPage/components/FinishedMatch/hooks/useEpisodes.tsx index cdf90415..d8524f79 100644 --- a/src/features/MatchPage/components/FinishedMatch/hooks/useEpisodes.tsx +++ b/src/features/MatchPage/components/FinishedMatch/hooks/useEpisodes.tsx @@ -40,7 +40,8 @@ export const useEpisodes = () => { settings: popupSettings, sportType, }).then(setEpisodes) - } else if (playlistOption.type === PlaylistTypes.MATCH) { + } else if (playlistOption.type === PlaylistTypes.MATCH + || playlistOption.type === PlaylistTypes.EVENT) { setEpisodes(playlistOption.data) } }, [matchId, sportType]) diff --git a/src/features/MatchPage/components/FinishedMatch/hooks/useEvents.tsx b/src/features/MatchPage/components/FinishedMatch/hooks/useEvents.tsx new file mode 100644 index 00000000..e1eb74f9 --- /dev/null +++ b/src/features/MatchPage/components/FinishedMatch/hooks/useEvents.tsx @@ -0,0 +1,28 @@ +import { useEffect, useState } from 'react' + +import type { Events } from 'requests' +import { getMatchEvents } from 'requests' + +import { usePageParams } from 'hooks/usePageParams' + +import { useEventsLexics } from './useEventsLexics' + +export const useEvents = () => { + const [events, setEvents] = useState([]) + const { fetchLexics } = useEventsLexics() + const { profileId: matchId, sportType } = usePageParams() + + useEffect(() => { + getMatchEvents({ + matchId, + sportType, + }).then(fetchLexics) + .then(setEvents) + }, [ + fetchLexics, + matchId, + sportType, + ]) + + return { events } +} diff --git a/src/features/MatchPage/components/FinishedMatch/hooks/useEventsLexics.tsx b/src/features/MatchPage/components/FinishedMatch/hooks/useEventsLexics.tsx new file mode 100644 index 00000000..cc886d0f --- /dev/null +++ b/src/features/MatchPage/components/FinishedMatch/hooks/useEventsLexics.tsx @@ -0,0 +1,25 @@ +import { useCallback } from 'react' + +import isEmpty from 'lodash/isEmpty' +import map from 'lodash/map' +import uniq from 'lodash/uniq' + +import type { Events } from 'requests' + +import { useLexicsStore } from 'features/LexicsStore' + +export const useEventsLexics = () => { + const { addLexicsConfig } = useLexicsStore() + + const fetchLexics = useCallback((events: Events) => { + const lexics = uniq(map(events, ({ l }) => l)) + + if (!isEmpty(lexics)) { + addLexicsConfig(lexics) + } + + return events + }, [addLexicsConfig]) + + return { fetchLexics } +} diff --git a/src/features/MatchPage/components/FinishedMatch/index.tsx b/src/features/MatchPage/components/FinishedMatch/index.tsx index ab5baf12..1fd98b91 100644 --- a/src/features/MatchPage/components/FinishedMatch/index.tsx +++ b/src/features/MatchPage/components/FinishedMatch/index.tsx @@ -17,6 +17,7 @@ export const FinishedMatch = (props: Props) => { const { chapters, closeSettingsPopup, + events, isSettingsPopupOpen, onPlayingChange, onPlaylistSelect, @@ -51,6 +52,7 @@ export const FinishedMatch = (props: Props) => { {playlists && ( diff --git a/src/features/MatchSidePlaylists/components/EventButton/index.tsx b/src/features/MatchSidePlaylists/components/EventButton/index.tsx new file mode 100644 index 00000000..6d0ca0dc --- /dev/null +++ b/src/features/MatchSidePlaylists/components/EventButton/index.tsx @@ -0,0 +1,88 @@ +import { ProfileTypes } from 'config' + +import type { Event, Team } from 'requests' + +import { usePageParams } from 'hooks/usePageParams' + +import type { PlayerPlaylistOption } from 'features/MatchPage/types' +import { Name } from 'features/Name' +import { T9n } from 'features/T9n' + +import { + Avatar, + Button, + EventInfo, + EventDesc, + Title, + SubTitle, + EventTime, +} from '../TabEvents/styled' + +type Props = { + active?: boolean, + disabled?: boolean, + event: Event, + isHomeTeam: boolean, + onClick: () => void, + player?: PlayerPlaylistOption, + team?: Team, +} + +export const EventButton = ({ + active, + disabled, + event, + isHomeTeam, + onClick, + player, + team, +}: Props) => { + const { sportType } = usePageParams() + const { + c: clearTime, + l: lexica, + p: playerId, + sc1: score1, + sc2: score2, + t: teamId, + } = event + + const avatarId = playerId || teamId + const profileType = playerId + ? ProfileTypes.PLAYERS + : ProfileTypes.TEAMS + const nameObj = player || team + + return ( + + ) +} diff --git a/src/features/MatchSidePlaylists/components/EventsList/index.tsx b/src/features/MatchSidePlaylists/components/EventsList/index.tsx new file mode 100644 index 00000000..29ff5ba0 --- /dev/null +++ b/src/features/MatchSidePlaylists/components/EventsList/index.tsx @@ -0,0 +1,63 @@ +import find from 'lodash/find' +import map from 'lodash/map' + +import type { Events, MatchInfo } from 'requests' + +import type { PlaylistOption, Playlists } from 'features/MatchPage/types' +import { PlaylistTypes } from 'features/MatchPage/types' + +import { isEqual } from '../../helpers' +import { EventButton } from '../EventButton' +import { Event, List } from '../TabEvents/styled' + +type Props = { + events: Events, + onSelect: (option: PlaylistOption) => void, + playlists: Playlists, + profile: MatchInfo, + selectedPlaylist?: PlaylistOption, +} + +export const EventsList = ({ + events, + onSelect, + playlists, + profile, + selectedPlaylist, +}: Props) => ( + + {map(events, (event) => { + const eventPlaylist = { + data: [{ + e: event.e, + h: event.h, + s: event.s, + }], + id: event.n, + type: PlaylistTypes.EVENT, + } as PlaylistOption + + const isHomeTeam = event.t === profile?.team1.id + const team = isHomeTeam + ? profile?.team1 + : profile?.team2 + const players = isHomeTeam + ? playlists.players.team1 + : playlists.players.team2 + const player = find(players, { id: event.p }) + + return ( + + onSelect(eventPlaylist)} + player={player} + team={team} + /> + + ) + })} + +) diff --git a/src/features/MatchSidePlaylists/components/TabEvents/index.tsx b/src/features/MatchSidePlaylists/components/TabEvents/index.tsx new file mode 100644 index 00000000..91863c2c --- /dev/null +++ b/src/features/MatchSidePlaylists/components/TabEvents/index.tsx @@ -0,0 +1,119 @@ +import { Fragment, useMemo } from 'react' + +import groupBy from 'lodash/groupBy' +import isEmpty from 'lodash/isEmpty' +import map from 'lodash/map' +import reverse from 'lodash/reverse' +import values from 'lodash/values' + +import type { Events, MatchInfo } from 'requests' + +import { useToggle } from 'hooks' + +import type { PlaylistOption, Playlists } from 'features/MatchPage/types' +import { T9n } from 'features/T9n' + +import { EventsList } from '../EventsList' +import { + BlockTitle, + Wrapper, + HalfList, + HalfEvents, + Tabs, + Tab, +} from './styled' + +type Props = { + events: Events, + onSelect: (option: PlaylistOption) => void, + playlists: Playlists, + profile: MatchInfo, + selectedPlaylist?: PlaylistOption, +} + +export const TabEvents = ({ + events, + onSelect, + playlists, + profile, + selectedPlaylist, +}: Props) => { + const { + close: setUnreversed, + isOpen: areEventsReversed, + open: setReversed, + } = useToggle() + + const groupedEvents = useMemo(() => values( + groupBy( + areEventsReversed ? reverse([...events]) : events, + 'h', + ), + ), [areEventsReversed, events]) + + if (!profile) return null + + return ( + + {isEmpty(events) + ? ( + + + + ) + : ( + + + + + + + + + + + { + map( + areEventsReversed + ? reverse(groupedEvents) + : groupedEvents, + (halfEvents, idx) => { + const firstEvent = halfEvents[0] + const isFirstBlock = idx === 0 + + return ( + + { + !isFirstBlock && ( + + + + ) + } + + + + + + ) + }, + ) + } + + + )} + + ) +} diff --git a/src/features/MatchSidePlaylists/components/TabEvents/styled.tsx b/src/features/MatchSidePlaylists/components/TabEvents/styled.tsx new file mode 100644 index 00000000..24368bb5 --- /dev/null +++ b/src/features/MatchSidePlaylists/components/TabEvents/styled.tsx @@ -0,0 +1,143 @@ +import styled, { css } from 'styled-components/macro' + +import { ProfileLogo } from 'features/ProfileLogo' + +import { Tabs as TabsBase, Tab as TabBase } from '../PlayersPlaylists/styled' +import { + BlockTitle as BlockTitleBase, + Button as ButtonBase, +} from '../../styled' + +export const Wrapper = styled.div` + display: flex; + flex-direction: column; + align-items: center; +` + +export const HalfList = styled.ul` + width: 100%; +` + +export const HalfEvents = styled.li` + :not(:last-child) { + margin-bottom: 12px; + } +` + +export const List = styled.ul` + margin-top: 12px; +` + +export const Event = styled.li` + width: 100%; + height: 48px; + + :not(:last-child) { + margin-bottom: 12px; + } +` + +type AvatarProps = { + isHomeTeam: boolean, + isPlayer: boolean, +} + +export const Avatar = styled(ProfileLogo)` + position: absolute; + width: 35px; + height: 43px; + object-fit: cover; + + ${({ isHomeTeam }) => ( + isHomeTeam + ? css`left: 5px;` + : css`right: 5px;` + )} + + ${({ isPlayer }) => ( + isPlayer + ? css`bottom: 0;` + : css` + top: 50%; + transform: translateY(-50%); + ` + )} +` + +export const EventInfo = styled.div` + display: flex; + width: 100%; +` + +export const EventDesc = styled.div` + position: relative; + display: flex; + flex-direction: column; + align-items: flex-start; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +` + +export const Title = styled.span` + font-weight: 600; + font-size: 14px; + line-height: 15px; + color: #ffffff; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + text-align: left; +` + +export const SubTitle = styled(Title)` + font-size: 10px; + line-height: 16px; + font-weight: normal; + color: rgba(255, 255, 255, 0.7); + margin-top: 2px; +` + +export const EventTime = styled(Title)` + font-size: 14px; + font-weight: normal; + margin-right: 10px; + width: auto; + overflow: initial; + min-width: 29px; +` + +export const Tabs = styled(TabsBase)` + margin-top: 0px; + margin-bottom: 18px; +` + +export const Tab = styled(TabBase)` + text-transform: none; + + :first-child { + margin-right: 15px; + } +` + +export const BlockTitle = styled(BlockTitleBase)` + text-align: center; + width: 100%; + display: inline-block; +` + +type ButtonProps = { + isHomeTeam: boolean, +} + +export const Button = styled(ButtonBase)` + &:hover ${EventDesc} { + overflow: visible; + } + + ${({ isHomeTeam }) => ( + isHomeTeam + ? css`padding-left: 60px;` + : css`padding: 0 40px 0 60px;` + )} +` diff --git a/src/features/MatchSidePlaylists/index.tsx b/src/features/MatchSidePlaylists/index.tsx index 184c5e4b..be14a1d0 100644 --- a/src/features/MatchSidePlaylists/index.tsx +++ b/src/features/MatchSidePlaylists/index.tsx @@ -1,10 +1,11 @@ -import type { MatchInfo } from 'requests' +import type { Events, MatchInfo } from 'requests' import type { PlaylistOption, Playlists } from 'features/MatchPage/types' import { Tab, TabsGroup } from 'features/Common' import { T9n } from 'features/T9n' import { Tabs } from './config' +import { TabEvents } from './components/TabEvents' import { TabWatch } from './components/TabWatch' import { useMatchSidePlaylists } from './hooks' import { @@ -15,11 +16,12 @@ import { const tabPanes = { [Tabs.WATCH]: TabWatch, - [Tabs.EVENTS]: () => null, + [Tabs.EVENTS]: TabEvents, [Tabs.LANGUAGES]: () => null, } type Props = { + events: Events, onSelect: (option: PlaylistOption) => void, playlists: Playlists, profile: MatchInfo, @@ -27,6 +29,7 @@ type Props = { } export const MatchSidePlaylists = ({ + events, onSelect, playlists, profile, @@ -50,7 +53,6 @@ export const MatchSidePlaylists = ({ onTabClick(Tabs.EVENTS)} > @@ -67,6 +69,7 @@ export const MatchSidePlaylists = ({ + +type Response = { + data?: Events, +} + +export const getMatchEvents = async ({ + matchId, + sportType, +}: Args) => { + const config = { + body: { + params: { + _p_match_id: matchId, + }, + proc, + }, + } + + const response: Response = await callApi({ + config, + url: `${DATA_URL}/${getSportLexic(sportType)}`, + }) + + return response?.data || [] +} diff --git a/src/requests/getMatchInfo.tsx b/src/requests/getMatchInfo.tsx index c6ef2d98..a9325de1 100644 --- a/src/requests/getMatchInfo.tsx +++ b/src/requests/getMatchInfo.tsx @@ -7,7 +7,7 @@ import { callApi } from 'helpers' const proc = PROCEDURES.get_match_info -type Team = { +export type Team = { abbrev_eng: string, abbrev_rus: string, id: number, diff --git a/src/requests/getUnauthenticatedMatch.tsx b/src/requests/getUnauthenticatedMatch.tsx new file mode 100644 index 00000000..5c54db9d --- /dev/null +++ b/src/requests/getUnauthenticatedMatch.tsx @@ -0,0 +1,42 @@ +import { + DATA_URL, + PROCEDURES, + SportTypes, +} from 'config' + +import { callApi } from 'helpers' + +const proc = PROCEDURES.landing_get_match_info + +type Team = { + name_eng: string, + name_rus: string, +} + +export type UnauthenticatedMatch = { + date: string, + team1: Team, + team2: Team, + tournament: { + name_eng: string, + name_rus: string, + }, +} | null + +export const getUnauthenticatedMatch = (sportId: SportTypes, matchId: number) +: Promise => { + const config = { + body: { + params: { + _p_match_id: matchId, + _p_sport: sportId, + }, + proc, + }, + } + + return callApi({ + config, + url: DATA_URL, + }) +} diff --git a/src/requests/index.tsx b/src/requests/index.tsx index 4f261fbc..b5d0cd7b 100644 --- a/src/requests/index.tsx +++ b/src/requests/index.tsx @@ -11,6 +11,7 @@ export * from './getTournamentInfo' export * from './getTeamInfo' export * from './getUserInfo' export * from './getMatchInfo' +export * from './getUnauthenticatedMatch' export * from './reportPlayerProgress' export * from './getVideos' export * from './saveUserInfo' @@ -18,6 +19,7 @@ export * from './getPlayerInfo' export * from './getMatchLastWatchSeconds' export * from './getMatchesPreviewImages' export * from './getSportActions' +export * from './getMatchEvents' export * from './getMatchPlaylists' export * from './getPlayerPlaylists' export * from './getSubscriptions'