{"version":3,"sources":["client/routes.ts","client/components/Logo/Logo.tsx","client/components/Logo/index.ts","client/hooks/useQueryParams.ts","client/hooks/redux.ts","client/selectors/sessionSelectors.ts","client/util/actionCreator.ts","server/shared/client/ApiConfig.ts","server/shared/client/ApiFetch.ts","server/shared/client/MoviesApi.ts","server/shared/client/StatusesApi.ts","server/shared/client/TokenApi.ts","server/shared/client/UsersApi.ts","server/shared/client/helpers.ts","client/util/api.ts","client/actions/sessionActions.ts","client/pages/Login/Login.tsx","client/pages/Login/index.ts","client/selectors/uiSelectors.ts","client/util/auth.ts","client/hooks/useRouteAuthInfo.ts","client/components/AuthRoute/AuthRoute.tsx","client/components/AuthRoute/index.ts","client/theme.ts","client/components/Avatar/Avatar.tsx","client/components/Avatar/index.ts","client/components/Icon/iconManifest.tsx","client/components/Icon/Icon.tsx","client/components/Icon/index.ts","client/components/Emoji/emojiManifest.tsx","client/components/Emoji/Emoji.tsx","client/components/Emoji/index.ts","client/components/MovieCard/MovieReactions.tsx","client/util/getAvailableReactions.ts","client/components/MovieCard/MovieCard.tsx","client/components/Tabs/Tabs.tsx","client/util/math.ts","client/components/Tabs/TabLink.tsx","client/components/Tabs/index.ts","client/pages/Movies/actionTypes.ts","client/pages/Movies/reducer.ts","client/pages/Movies/actions.ts","client/actions/uiActions.ts","client/components/Modal/Modal.tsx","client/components/Modal/index.ts","client/pages/Movies/components/ReactionModal.tsx","client/selectors/movieSelectors.ts","client/components/Paginator/Paginator.tsx","client/components/Paginator/index.ts","client/pages/Movies/Movies.tsx","client/pages/Movies/index.ts","client/components/Input/Input.tsx","client/components/Input/index.ts","client/actions/movieActions.ts","client/components/NewMovieOverlay/NewMovieOverlay.tsx","client/components/NewMovieOverlay/index.ts","client/components/BottomNav/BottomNav.tsx","client/components/BottomNav/index.ts","client/pages/SearchMovies/actionTypes.ts","client/pages/SearchMovies/actions.ts","client/pages/SearchMovies/reducer.ts","client/util/debounce.ts","client/components/MovieCard/index.ts","client/pages/SearchMovies/components/StatusMovieList.tsx","client/pages/SearchMovies/SearchMovies.tsx","client/pages/SearchMovies/index.ts","client/pages/AuthPages.tsx","client/App.tsx","client/reportWebVitals.ts","client/reducers/sessionReducer.ts","client/reducers/uiReducer.ts","client/reducers/movieReducer.ts","client/store.ts","index.tsx"],"names":["ROUTES","login","movies","SIZES","tiny","small","medium","large","reelRotate","keyframes","AnimatedReel","styled","path","animate","css","GradientFill","offset","stopColor","stopOpacity","Logo","size","width","viewBox","version","xmlns","fillRule","clipRule","strokeLinecap","strokeLinejoin","strokeMiterlimit","id","d","fill","style","transformOrigin","stroke","strokeWidth","x1","y1","x2","y2","gradientUnits","gradientTransform","useQueryParams","URLSearchParams","useLocation","search","useAppDispatch","useDispatch","useAppSelector","useSelector","selectSessionState","state","session","selectToken","token","selectUser","user","actionCreator","type","payload","undefined","ApiConfig","baseEndpoint","headers","ApiFetch","response","asText","status","statusText","text","json","body","route","method","fetch","this","getEndpoint","JSON","stringify","ok","generateError","get","includes","errorResponse","bodyResponse","MoviesApi","statusId","options","page","pageSize","order","movieId","query","StatusesApi","TokenApi","UsersApi","isErrorResponse","r","sessionActions","error","Wrapper","div","ErrorMessage","p","Login","sessionState","reduxDispatch","history","useHistory","useEffect","tokenLoading","tokenError","dispatch","a","authorization","window","localStorage","setItem","replace","selectModalOpen","ui","modalOpen","isLoggedIn","isFuture","toDate","exp","useRouteAuthInfo","memoryToken","tokenInfo","getItem","parse","getStoredTokenInfo","location","hasValidToken","targetRoute","pathname","isFetchingUser","LoadingWrapper","AuthRoute","props","children","to","theme","primary","p900","Color","p600","p500","p400","accent","a900","a600","a500","a400","tertiary","t500","neutral","black","n900","n800","n700","n600","n500","n400","n300","n200","n100","white","red","r600","r500","r400","r100","blue","b500","colorStates","disabled","default","focus","hover","danger","rgba","c","alpha","object","g","b","COLORS","AvatarWrapper","colorFill","hex","pickColor","s","num","split","reduce","sum","e","charCodeAt","length","Avatar","imageLink","alt","src","charAt","toUpperCase","iconManifest","add","height","addReaction","movie","next","play","previous","getHexColor","color","Error","Icon","icon","emojiManifest","checkMark","crossMark","faceVomiting","cx","cy","transform","grinningFace","rx","ry","loudlyCryingFace","partyFace","poop","slightSmile","startStruck","thumbsDown","thumbsUp","yawningFace","EMOJI_SIZES","Emoji","emoji","rest","ReactionWrapper","MovieReactions","reactions","map","className","active","reaction","count","getAvailableReactions","Movie","MovieHeader","section","MovieTitle","h6","MovieOwner","MovieDetails","IconButton","button","formatAddedOn","format","watchedOn","addedOn","MovieCard","onAddReactionClick","reactionsMap","sort","r1","r2","indexOf","sortReactions","all","Object","keys","userReaction","name","onClick","displayName","avatarLink","INDICATOR_WIDTH","INDICATOR_HEIGHT","Container","TabContainer","generateAnimation","currentLeft","finalLeft","TabIndicator","span","left","animation","animationLength","min","max","value","Math","Tabs","onChange","propsChildren","tabsContainerRef","useRef","useState","mounted","setMounted","indicatorStyle","setIndicatorStyle","tabValues","Map","React","child","i","childValue","selected","set","tabsData","ref","valueMap","childIndex","selectedChild","tabDimensions","getBoundingClientRect","getTabChildData","current","abs","TabLink","aria-selected","GET_STATUSES_START","GET_STATUSES_END","GET_STATUSES_ERROR","GET_MOVIES_START","GET_MOVIES_END","GET_MOVIES_ERROR","ADD_REACTION_START","ADD_REACTION_END","ADD_REACTION_ERROR","UPDATE_MOVIE_START","UPDATE_MOVIE_END","UPDATE_MOVIE_ERROR","SET_MOVIE_MODAL","SET_STATUS","moviesDefaultState","getStatusesLoading","statuses","getStatusesError","getMoviesLoading","getMoviesError","addReactionLoading","addReactionError","updateMovieLoading","updateMovieError","selectedStatus","movieModal","currentPage","totalCount","pageCount","moviesReducer","action","at","data","moviesActions","getMoviesByStatus","resp","changeStatus","getStatuses","sortedStatuses","s1","s2","markAsWatched","targetStatus","find","uiActions","modalId","closeModal","modalRoot","document","getElementById","ModalWrapper","allowClickThrough","Backdrop","ModalContent","verticalPosition","Modal","show","onBackdropClick","clickThrough","openModal","ReactDOM","createPortal","Title","h3","ButtonContainer","ReactButton","modalTitle","ReactionModal","onReaction","selectLastMovieAdded","lastMovieAdded","selectAddMovieLoading","addMovieLoading","selectAddMovieError","addMovieError","Pages","PagePlaceholder","PageButton","ArrowButtons","generatePageButton","callback","generatePageButtons","totalPages","Array","_","MAX_PAGES","startingPages","endingPages","reverse","Paginator","onPageClick","Statuses","MovieList","MovieListScroll","PageInfo","renderPageInfo","totalItems","start","end","Movies","useReducer","pageOptions","setStatusById","v","m","movieStatus","Label","label","StyledInput","input","hasError","renderMessage","Input","forwardRef","propsOnChange","inputType","target","htmlFor","movieActions","addMovie","movieName","form","NewMovieOverlay","setMovieName","onSubmit","preventDefault","autoFocus","placeholder","nav","NavLink","Link","Fab","BottomNav","setModalOpen","activeClassName","SEARCH_MOVIES_START","SEARCH_MOVIES_END","SEARCH_MOVIES_ERROR","SET_QUERY","searchMoviesActions","searchMovies","moviesWithStatus","sorted","existing","k","defaultSearchMoviesState","searchMoviesLoading","searchMoviesError","searchMoviesReducer","debounce","func","debounceTime","timeout","args","later","apply","clearTimeout","setTimeout","statusColor","getStatusColor","StatusMovieList","openMovieModal","statusList","InputWrapper","debouncedSearch","SearchMovies","AUTH_ROUTES","ALL_ROUTES","PageContent","childHeight","AuthPages","pageContent","contentHeight","clientHeight","currentHeight","setCurrentHeight","blurStyling","AppWrapper","App","exact","reportWebVitals","onPerfEntry","Function","then","getCLS","getFID","getFCP","getLCP","getTTFB","defaultSessionState","userLoading","userError","sessionReducer","defaultUiState","uiReducer","defaultMovieState","movieReducer","store","configureStore","reducer","middleware","getDefaultMiddleware","concat","thunk","devTools","process","i18n","use","Backend","LanguageDetector","initReactI18next","init","fallbackLng","debug","ns","defaultNS","preload","interpolation","escapeValue","render","StrictMode","fallback"],"mappings":"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","file":"static/js/main.a6bf9ffa.chunk.js","sourcesContent":["const ROUTES = {\n login: '/login',\n movies: '/movies',\n};\n\nexport default ROUTES;\n","import React from 'react';\nimport styled, { css, keyframes } from 'styled-components';\n\ntype LogoSizes = 'tiny' | 'small' | 'medium' | 'large';\n\nconst SIZES: { [S in LogoSizes]: string } = {\n tiny: '25px',\n small: '50px',\n medium: '75px',\n large: '100px',\n};\n\nconst reelRotate = keyframes`\n 100% {\n transform: rotate(360deg);\n }\n`;\n\ninterface AnimateProps {\n animate?: boolean;\n}\n\nconst AnimatedReel = styled.path`\n animation: ${({ animate }) =>\n animate\n ? css`\n ${reelRotate} 2s linear infinite\n `\n : 'none'};\n`;\n\ninterface Props {\n size?: LogoSizes;\n animate?: boolean;\n}\n\nconst GradientFill = () => (\n <>\n \n \n \n \n);\n\nconst Logo = ({ size = 'small', animate = false }: Props) => (\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n);\n\nexport default Logo;\n","import Logo from './Logo';\n\nexport default Logo;\n","import { useLocation } from 'react-router-dom';\n\nconst useQueryParams = () => new URLSearchParams(useLocation().search);\n\nexport default useQueryParams;\n","import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';\nimport { AppDispatch, RootState } from '../store';\n\nexport const useAppDispatch = () => useDispatch();\nexport const useAppSelector: TypedUseSelectorHook = useSelector;\n","import { RootState } from '../store';\n\nexport const selectSessionState = (state: RootState) => state.session;\nexport const selectToken = (state: RootState) => state.session.token;\nexport const selectUser = (state: RootState) => state.session.user;\n","import { ActionCreatorsMapObject } from 'redux';\n\nexport interface Action {\n type: T;\n}\n\nexport interface ActionWithPayload extends Action {\n payload: P;\n}\n\ntype ActionCreator = {\n (type: T): Action;\n (type: T, payload: P): ActionWithPayload;\n};\n\nconst actionCreator: ActionCreator = (\n type: T,\n payload?: P,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): any => (payload !== undefined ? { type, payload } : { type });\n\nexport type Actions = ReturnType;\n\nexport default actionCreator;\n","import { RequestInit } from 'node-fetch';\n\nclass Config {\n baseEndpoint: string | null = null;\n\n headers: RequestInit['headers'] = undefined;\n}\n\n// eslint-disable-next-line import/prefer-default-export\nexport const ApiConfig = new Config();\n","import fetch, { RequestInit, Response } from 'node-fetch';\nimport { ApiConfig } from './ApiConfig';\n\nexport interface ApiError {\n status: number;\n statusText: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n body: any;\n}\n\nexport class ApiFetch {\n static async generateError(\n response: Response,\n asText = false,\n ): Promise {\n return {\n status: response.status,\n statusText: response.statusText,\n body: asText ? await response.text() : await response.json(),\n };\n }\n\n static getEndpoint(route: string) {\n if (ApiConfig.baseEndpoint) {\n return `${ApiConfig.baseEndpoint}${route}`;\n }\n return route;\n }\n\n static async fetch(\n route: string,\n method: RequestInit['method'],\n body?: Body,\n ) {\n const response = await fetch(this.getEndpoint(route), {\n method,\n headers: {\n ...(ApiConfig.headers || {}),\n 'content-type': 'application/json',\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const errorResponse = await this.generateError(\n response,\n !response.headers.get('content-type')?.includes('application/json'),\n );\n return errorResponse;\n }\n\n if (response.status === 204) {\n return undefined as unknown as Res;\n }\n\n const bodyResponse: Res = await response.json();\n return bodyResponse;\n }\n}\n","import {\n CreateMovieRequest,\n MovieResponse,\n UpdateMovieRequest,\n} from '../movie';\nimport { UpdateMovieReactionRequest } from '../reaction';\nimport { PaginatedResponse } from '../utility';\nimport { ApiFetch } from './ApiFetch';\n\n// eslint-disable-next-line import/prefer-default-export\nexport const MoviesApi = {\n getAll: () => ApiFetch.fetch('/movies', 'GET'),\n create: (body: CreateMovieRequest) =>\n ApiFetch.fetch('/movies', 'POST', body),\n getByStatus: (\n statusId: string,\n options: { pageSize: number; page: number; order: 'ASC' | 'DESC' },\n ) =>\n ApiFetch.fetch>(\n `/movies/status/${statusId}?page=${options.page}&pageSize=${options.pageSize}&order=${options.order}`,\n 'GET',\n ),\n updateReaction: (movieId: string, body: UpdateMovieReactionRequest) =>\n ApiFetch.fetch(\n `/movies/${movieId}/reaction`,\n 'PUT',\n body,\n ),\n update: (movieId: string, body: UpdateMovieRequest) =>\n ApiFetch.fetch(\n `/movies/${movieId}`,\n 'PATCH',\n body,\n ),\n search: (query: string) =>\n ApiFetch.fetch(`/movies/search?query=${query}`, 'GET'),\n};\n","import { StatusResponse } from '../status';\nimport { ApiFetch } from './ApiFetch';\n\n// eslint-disable-next-line import/prefer-default-export\nexport const StatusesApi = {\n getAll: () => ApiFetch.fetch('/statuses', 'GET'),\n getById: (statusId: string) =>\n ApiFetch.fetch(`/statuses/${statusId}`, 'GET'),\n};\n","import { TokenResponse } from '../token';\nimport { ApiFetch } from './ApiFetch';\n\n// eslint-disable-next-line import/prefer-default-export\nexport const TokenApi = {\n create: () => ApiFetch.fetch('/token', 'POST'),\n};\n","import { ApiFetch } from './ApiFetch';\nimport { UserResponse } from '../user';\n\n// eslint-disable-next-line import/prefer-default-export\nexport const UsersApi = {\n getCurrentUser: () => ApiFetch.fetch('/users/@me', 'GET'),\n};\n","import { ApiError } from './ApiFetch';\n\nexport const isErrorResponse = >(\n response: K | ApiError,\n): response is ApiError => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const r = response as any;\n\n if (!r) {\n return false;\n }\n\n if (r.status && r.statusText) {\n return true;\n }\n\n return false;\n};\n\nexport const isSuccessResponse = >(\n response: K | ApiError,\n): response is K => !isErrorResponse(response);\n","import { ApiConfig } from '../../server/shared/client/ApiConfig';\n\nApiConfig.baseEndpoint = '/api';\n\nexport * from '../../server/shared/client';\n","import actionCreator, { Actions } from '../util/actionCreator';\nimport { TokenResponse } from '../../server/shared/token';\nimport { AppThunk } from '../store';\nimport { UserResponse } from '../../server/shared/user';\nimport { ApiConfig, isErrorResponse, TokenApi, UsersApi } from '../util/api';\n\nconst GET_TOKEN_START = 'GET_TOKEN_START';\nconst GET_TOKEN_END = 'GET_TOKEN_END';\nconst GET_TOKEN_ERROR = 'GET_TOKEN_ERROR';\nconst GET_CURRENT_USER_START = 'GET_CURRENT_USER_START';\nconst GET_CURRENT_USER_END = 'GET_CURRENT_USER_END';\nconst GET_CURRENT_USER_ERROR = 'GET_CURRENT_USER_ERROR';\n\nexport const sessionActions = {\n getTokenStart: () => actionCreator(GET_TOKEN_START),\n getTokenEnd: (payload: TokenResponse) =>\n actionCreator(GET_TOKEN_END, payload),\n getTokenError: (error: string) => actionCreator(GET_TOKEN_ERROR, error),\n getCurrentUserStart: () => actionCreator(GET_CURRENT_USER_START),\n getCurrentUserEnd: (user: UserResponse) =>\n actionCreator(GET_CURRENT_USER_END, user),\n getCurrentUserError: (error: string) =>\n actionCreator(GET_CURRENT_USER_ERROR, error),\n};\n\nexport type SessionActions = Actions;\n\nexport const getCurrentUser = (): AppThunk => async (dispatch) => {\n dispatch(sessionActions.getCurrentUserStart());\n const response = await UsersApi.getCurrentUser();\n if (isErrorResponse(response)) {\n dispatch(sessionActions.getCurrentUserError(response.statusText));\n return;\n }\n dispatch(sessionActions.getCurrentUserEnd(response));\n};\n\nexport const login =\n (token: string): AppThunk =>\n async (dispatch) => {\n dispatch(sessionActions.getTokenStart());\n ApiConfig.headers = {\n authorization: `Bearer ${token}`,\n };\n const response = await TokenApi.create();\n ApiConfig.headers = undefined;\n if (isErrorResponse(response)) {\n dispatch(sessionActions.getTokenError(response.statusText));\n return;\n }\n window.localStorage.setItem('token', JSON.stringify(response));\n dispatch(sessionActions.getTokenEnd(response));\n };\n","import React, { useEffect } from 'react';\nimport { useHistory } from 'react-router-dom';\nimport styled from 'styled-components';\nimport Logo from '../../components/Logo';\nimport useQueryParams from '../../hooks/useQueryParams';\nimport { useAppDispatch, useAppSelector } from '../../hooks/redux';\nimport { selectSessionState } from '../../selectors/sessionSelectors';\nimport { login } from '../../actions/sessionActions';\nimport ROUTES from '../../routes';\n\nconst Wrapper = styled.div`\n min-height: 100vh;\n width: 100%;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n background-color: rgb(21, 32, 49);\n`;\n\nconst ErrorMessage = styled.p`\n color: tomato;\n`;\n\nconst TOKEN_QUERY_PARAM = 'token';\n\nconst Login = () => {\n const queryParams = useQueryParams();\n const token = queryParams.get(TOKEN_QUERY_PARAM) || null;\n const sessionState = useAppSelector(selectSessionState);\n const reduxDispatch = useAppDispatch();\n const history = useHistory();\n\n useEffect(() => {\n if (\n token &&\n !sessionState.tokenLoading &&\n !sessionState.token &&\n !sessionState.tokenError\n ) {\n reduxDispatch(login(token));\n }\n }, [\n token,\n sessionState.tokenLoading,\n sessionState.token,\n sessionState.tokenError,\n ]);\n\n useEffect(() => {\n if (sessionState.token) {\n history.replace(ROUTES.movies);\n }\n }, [sessionState.token]);\n\n return (\n \n \n {sessionState.tokenError}\n \n );\n};\n\nexport default Login;\n","import Login from './Login';\n\nexport default Login;\n","import { RootState } from '../store';\n\n// eslint-disable-next-line import/prefer-default-export\nexport const selectModalOpen = (state: RootState) => state.ui.modalOpen;\n","import { isFuture, toDate } from 'date-fns';\nimport { TokenResponse } from '../../server/shared/token';\n\nexport const isLoggedIn = (token: TokenResponse | null) =>\n token && isFuture(toDate(token.exp));\n\nexport const getStoredTokenInfo = () => {\n const tokenInfo = window.localStorage.getItem('token');\n\n if (!tokenInfo) {\n return null;\n }\n\n try {\n return JSON.parse(tokenInfo) as TokenResponse;\n } catch {\n return null;\n }\n};\n","import { useEffect } from 'react';\nimport { useLocation } from 'react-router-dom';\nimport { useAppDispatch, useAppSelector } from './redux';\nimport { selectToken, selectUser } from '../selectors/sessionSelectors';\nimport { getStoredTokenInfo, isLoggedIn } from '../util/auth';\nimport { getCurrentUser, sessionActions } from '../actions/sessionActions';\n\nconst useRouteAuthInfo = () => {\n const token = useAppSelector(selectToken);\n const memoryToken = getStoredTokenInfo();\n const dispatch = useAppDispatch();\n const user = useAppSelector(selectUser);\n const location = useLocation();\n\n const fetchUserInfo = () => {\n if (isLoggedIn(token) && !user) {\n dispatch(getCurrentUser());\n }\n };\n\n const setTokenInfo = () => {\n if (isLoggedIn(memoryToken) && memoryToken && !token) {\n dispatch(sessionActions.getTokenEnd(memoryToken));\n }\n };\n\n useEffect(fetchUserInfo, [token, user, dispatch]);\n useEffect(setTokenInfo, [memoryToken, token, dispatch]);\n\n const hasValidToken = isLoggedIn(token || memoryToken);\n\n return {\n targetRoute: location.pathname,\n isLoggedIn: hasValidToken,\n isFetchingUser: user === null && hasValidToken,\n };\n};\n\nexport default useRouteAuthInfo;\n","import React from 'react';\nimport { Redirect, Route, RouteProps } from 'react-router-dom';\nimport styled from 'styled-components';\nimport ROUTES from '../../routes';\nimport useRouteAuthInfo from '../../hooks/useRouteAuthInfo';\nimport Logo from '../Logo';\n\nconst LoadingWrapper = styled.div`\n min-height: 100vh;\n width: 100%;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n background-color: rgb(21, 32, 49);\n`;\n\nconst AuthRoute: React.FunctionComponent = (props) => {\n const { children } = props;\n const { targetRoute, isLoggedIn, isFetchingUser } = useRouteAuthInfo();\n\n if (!isLoggedIn) {\n return ;\n }\n\n if (isFetchingUser) {\n return (\n \n \n \n );\n }\n\n return {children};\n};\n\nexport default AuthRoute;\n","import AuthRoute from './AuthRoute';\n\nexport default AuthRoute;\n","import Color from 'color';\n\nconst theme = {\n primary: {\n p900: Color('#02595e'),\n p600: Color('#0495a0'),\n p500: Color('#27edfe'),\n p400: Color('#7dd4ea'),\n },\n accent: {\n a900: Color('#43037b'),\n a600: Color('#6a12b8'),\n a500: Color('#af55f8'),\n a400: Color('#c083f5'),\n },\n tertiary: {\n t500: Color('#ff94d8'),\n },\n neutral: {\n black: Color('#06121a'),\n n900: Color('#152031'),\n n800: Color('#22283d'),\n n700: Color('#272d40'),\n n600: Color('#393b53'),\n n500: Color('#494b66'),\n n400: Color('#535b6d'),\n n300: Color('#737a8b'),\n n200: Color('#8e969a'),\n n100: Color('#bdc1c6'),\n white: Color('#fff'),\n },\n red: {\n r600: Color('#bd2a2a'),\n r500: Color('#de3e3e'),\n r400: Color('#ec5e5e'),\n r100: Color('#e38686'),\n },\n blue: {\n b500: Color('#58a1f5'),\n },\n};\n\nexport const colorStates = {\n primary: {\n disabled: theme.primary.p900,\n default: theme.primary.p500,\n focus: theme.primary.p400,\n hover: theme.primary.p600,\n },\n accent: {\n disabled: theme.accent.a900,\n default: theme.accent.a500,\n focus: theme.accent.a400,\n hover: theme.accent.a600,\n },\n danger: {\n disabled: theme.red.r100,\n default: theme.red.r500,\n focus: theme.red.r600,\n hover: theme.red.r400,\n },\n};\n\nexport const rgba = (c: Color, alpha = 0) => {\n const { r, g, b } = c.object();\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n};\n\nexport default theme;\n","import React from 'react';\nimport styled from 'styled-components';\nimport theme from '../../theme';\n\ninterface Props {\n imageLink: string | null;\n alt: string;\n children: string;\n}\n\nconst COLORS = ['#b2135d', '#704bc6', '#24af63', '#ce5056'];\n\ninterface WrapperProps {\n colorFill?: string;\n}\n\nconst AvatarWrapper = styled.div`\n height: 32px;\n width: 32px;\n overflow: hidden;\n border-radius: 50%;\n background-color: ${({ colorFill }) => colorFill || 'revert'};\n color: ${theme.neutral.n100.hex()};\n display: flex;\n justify-content: center;\n align-items: center;\n font-size: 18px;\n font-weight: 600;\n\n img {\n width: 100%;\n }\n`;\n\nconst pickColor = (s: string) => {\n const arr = s.split('');\n const num = arr.reduce((sum, e) => e.charCodeAt(0) + sum, 0);\n return COLORS[num % COLORS.length];\n};\n\nconst Avatar = (props: Props) => {\n const { imageLink, children, alt } = props;\n\n if (!imageLink) {\n return (\n \n {children.charAt(0).toUpperCase()}\n \n );\n }\n\n return (\n \n {alt}\n \n );\n};\n\nexport default Avatar;\n","import Avatar from './Avatar';\n\nexport default Avatar;\n","import React from 'react';\n\nexport const iconManifest = {\n add: (\n <>\n \n \n \n \n \n \n \n \n \n ),\n addReaction: (\n <>\n \n \n \n ),\n movie: (\n \n ),\n next: (\n <>\n \n \n \n ),\n play: (\n \n ),\n previous: (\n <>\n \n \n \n ),\n search: (\n <>\n \n \n \n ),\n};\n\nexport type IconManifest = keyof typeof iconManifest;\n","import React from 'react';\nimport { IconManifest, iconManifest } from './iconManifest';\nimport theme, { colorStates } from '../../theme';\n\nconst SIZES = {\n tiny: '20px',\n small: '24px',\n medium: '32px',\n large: '36px',\n};\n\ninterface Props {\n icon: IconManifest;\n color?: 'primary' | 'dark' | 'light' | 'accent' | 'danger';\n size?: keyof typeof SIZES;\n}\n\nconst getHexColor = (color: NonNullable) => {\n switch (color) {\n case 'dark':\n return theme.neutral.n500.hex();\n case 'light':\n return theme.neutral.n100.hex();\n case 'primary':\n return colorStates.primary.default.hex();\n case 'accent':\n return colorStates.accent.default.hex();\n case 'danger':\n return colorStates.danger.default.hex();\n default:\n throw new Error(`${color} is not a valid icon color`);\n }\n};\n\nconst Icon = (props: Props) => {\n const { icon, size = 'small', color = 'light' } = props;\n\n return (\n \n {iconManifest[icon]}\n \n );\n};\n\nexport default Icon;\n","import Icon from './Icon';\nimport { IconManifest } from './iconManifest';\n\nexport type IconType = IconManifest;\n\nexport default Icon;\n","import React from 'react';\n\nconst emojiManifest = {\n checkMark: (\n <>\n \n \n \n ),\n crossMark: (\n <>\n \n \n ),\n faceVomiting: (\n <>\n \n \n \n \n \n \n \n \n ),\n grinningFace: (\n <>\n \n \n \n \n \n \n ),\n loudlyCryingFace: (\n <>\n \n \n \n \n \n \n \n ),\n partyFace: (\n <>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ),\n poop: (\n <>\n \n \n \n \n \n \n \n \n ),\n slightSmile: (\n <>\n \n \n \n \n \n ),\n startStruck: (\n <>\n \n \n \n \n \n ),\n thumbsDown: (\n <>\n \n \n \n ),\n thumbsUp: (\n <>\n \n \n \n ),\n yawningFace: (\n <>\n \n \n \n \n \n ),\n};\n\nexport type EmojiType = keyof typeof emojiManifest;\n\nexport default emojiManifest;\n","import React from 'react';\nimport emojiManifest, { EmojiType } from './emojiManifest';\n\nconst EMOJI_SIZES = {\n tiny: '20px',\n small: '24px',\n medium: '32px',\n large: '36px',\n};\n\ninterface Props {\n emoji: EmojiType;\n size?: keyof typeof EMOJI_SIZES;\n className?: string;\n}\n\nconst Emoji = (props: Props) => {\n const { emoji, size = 'small', ...rest } = props;\n\n return (\n \n {emojiManifest[emoji]}\n \n );\n};\n\nexport default Emoji;\n","import Emoji from './Emoji';\nimport { EmojiType as ET } from './emojiManifest';\n\nexport type EmojiType = ET;\n\nexport default Emoji;\n","import React from 'react';\nimport styled from 'styled-components';\nimport Emoji, { EmojiType } from '../Emoji';\nimport theme from '../../theme';\n\ninterface ReactionData {\n reaction: EmojiType;\n count: number;\n active: boolean;\n}\n\ninterface Props {\n reactions: ReactionData[];\n}\n\nconst Wrapper = styled.div`\n display: flex;\n padding: 4px;\n border-radius: 4px;\n background-color: ${theme.neutral.n900.hex()};\n position: absolute;\n bottom: -16px;\n left: 8px;\n`;\n\nconst ReactionWrapper = styled.div`\n display: flex;\n align-items: center;\n font-size: 12px;\n color: ${theme.neutral.n200.hex()};\n\n &:not(:last-child) {\n margin-right: 8px;\n }\n\n p {\n margin: 0 0 0 4px;\n }\n\n &.active {\n p {\n color: ${theme.primary.p500.hex()};\n font-weight: 600;\n }\n }\n`;\n\nconst MovieReactions = (props: Props) => {\n const { reactions } = props;\n\n if (reactions.length <= 0) {\n return null;\n }\n\n return (\n \n {reactions.map((r) => (\n \n \n

{r.count}

\n \n ))}\n
\n );\n};\n\nexport default MovieReactions;\n","import { EmojiType } from '../components/Emoji';\nimport { MovieResponse } from '../../server/shared/movie';\n\nconst getAvailableReactions = (movie: MovieResponse): EmojiType[] => {\n if (!movie) {\n return [];\n }\n\n if (movie.status.order === 0) {\n return ['thumbsUp', 'thumbsDown'];\n }\n\n if (movie.status.order === 1) {\n return ['checkMark', 'crossMark'];\n }\n\n return [\n 'poop',\n 'faceVomiting',\n 'loudlyCryingFace',\n 'yawningFace',\n 'slightSmile',\n 'grinningFace',\n 'partyFace',\n 'startStruck',\n ];\n};\n\nexport default getAvailableReactions;\n","import React from 'react';\nimport { format, toDate } from 'date-fns';\nimport styled from 'styled-components';\nimport { MovieResponse } from '../../../server/shared/movie';\nimport theme from '../../theme';\nimport Avatar from '../Avatar';\nimport Icon from '../Icon';\nimport MovieReactions from './MovieReactions';\nimport { EmojiType } from '../Emoji';\nimport getAvailableReactions from '../../util/getAvailableReactions';\n\nconst Movie = styled.div`\n padding: 12px;\n background-color: ${theme.neutral.black.hex()};\n max-width: 500px;\n width: 100%;\n border-radius: 4px;\n position: relative;\n\n &:not(:last-child) {\n margin-bottom: 20px;\n }\n`;\n\nconst MovieHeader = styled.section`\n margin-bottom: 20px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n position: relative;\n`;\n\nconst MovieTitle = styled.h6`\n font-size: 18px;\n margin: 0;\n font-weight: 600;\n`;\n\nconst MovieOwner = styled.div`\n display: flex;\n align-items: center;\n\n p {\n margin: 0;\n }\n\n & > :first-child {\n margin-right: 8px;\n }\n`;\n\nconst MovieDetails = styled.section`\n display: flex;\n justify-content: space-between;\n align-items: center;\n font-size: 14px;\n color: ${theme.neutral.n200.hex()};\n border-top: 1px solid ${theme.neutral.n700.hex()};\n padding-top: 12px;\n\n p {\n margin: 0;\n }\n`;\n\nconst IconButton = styled.button`\n display: flex;\n padding: 4px;\n background-color: transparent;\n cursor: pointer;\n border: 0;\n\n &:hover {\n svg {\n fill: ${theme.neutral.n200.hex()};\n }\n }\n`;\n\ninterface Props {\n onAddReactionClick: () => void;\n movie: MovieResponse;\n}\n\nconst sortReactions = (movie: MovieResponse) => {\n const reactions = getAvailableReactions(movie);\n\n return movie.reactions.sort(\n (r1, r2) =>\n reactions.indexOf(r1.reaction as EmojiType) -\n reactions.indexOf(r2.reaction as EmojiType),\n );\n};\n\nconst formatAddedOn = (movie: MovieResponse) =>\n format(toDate(movie.watchedOn || movie.addedOn), 'MM/dd/yyyy h:mm aaa');\n\nconst MovieCard = (props: Props) => {\n const { movie, onAddReactionClick } = props;\n\n const reactionsMap = sortReactions(movie).reduce((all, r) => {\n if (all[r.reaction]) {\n return {\n ...all,\n [r.reaction]: all[r.reaction] + 1,\n };\n }\n return {\n ...all,\n [r.reaction]: 1,\n };\n }, {} as { [K: string]: number });\n\n const reactions = (Object.keys(reactionsMap) as EmojiType[]).map((r) => ({\n reaction: r,\n count: reactionsMap[r],\n active: movie.userReaction ? movie.userReaction.reaction === r : false,\n }));\n\n return (\n \n \n {movie.name}\n onAddReactionClick()}>\n \n \n \n\n \n

{formatAddedOn(movie)}

\n\n \n

{movie.user.displayName}

\n \n {movie.user.displayName}\n \n
\n
\n \n
\n );\n};\n\nexport default MovieCard;\n","import * as React from 'react';\nimport styled, { css, Keyframes, keyframes } from 'styled-components';\nimport { useEffect, useRef, useState } from 'react';\nimport { mathClamp } from '../../util/math';\nimport theme from '../../theme';\n\ntype TabValue = string | number;\n\ninterface Props {\n value: TabValue;\n onChange: (value: TabValue) => void;\n}\n\ninterface IndicatorStyles {\n left?: number;\n animation?: Keyframes;\n animationLength?: number;\n}\n\ninterface IndicatorProps {\n animation?: Keyframes;\n left?: number;\n animationLength?: number;\n}\n\nconst INDICATOR_HEIGHT = 4;\nconst INDICATOR_WIDTH = INDICATOR_HEIGHT * 4;\nconst ANIMATION_PIXEL_PER_SECOND = 1000;\n\nconst Container = styled.div`\n position: relative;\n`;\n\nconst TabContainer = styled.div``;\n\nconst generateAnimation = (currentLeft: number | null, finalLeft: number) => {\n if (currentLeft === null) {\n return undefined;\n }\n if (finalLeft > currentLeft) {\n return keyframes`\n 0% {\n left: ${currentLeft - INDICATOR_WIDTH / 2}px;\n width: ${INDICATOR_WIDTH}px;\n }\n \n 50% {\n left: ${currentLeft - INDICATOR_WIDTH / 2}px;\n width: ${finalLeft - currentLeft + INDICATOR_WIDTH}px;\n }\n \n 100% {\n left: ${finalLeft - INDICATOR_WIDTH / 2}px;\n width: ${INDICATOR_WIDTH}px;\n }\n `;\n }\n return keyframes`\n 0% {\n left: ${currentLeft - INDICATOR_WIDTH / 2}px;\n width: ${INDICATOR_WIDTH}px;\n }\n \n 50% {\n left: ${finalLeft - INDICATOR_WIDTH / 2}px;\n width: ${currentLeft - finalLeft + INDICATOR_WIDTH}px;\n }\n \n 100% {\n left: ${finalLeft - INDICATOR_WIDTH / 2}px;\n width: ${INDICATOR_WIDTH}px;\n }\n `;\n};\n\nconst TabIndicator = styled.span`\n display: block;\n background-color: ${theme.accent.a500.hex()};\n height: ${INDICATOR_HEIGHT}px;\n width: ${INDICATOR_WIDTH}px;\n border-radius: ${INDICATOR_HEIGHT / 2}px;\n position: relative;\n left: ${(props) => (props.left ? props.left - INDICATOR_WIDTH / 2 : 0)}px;\n transition: opacity 500ms ease;\n ${(props) => {\n if (props.animation && props.animationLength) {\n return css`\n animation: ${props.animation}\n ${mathClamp(200, 300, props.animationLength)}ms\n cubic-bezier(0.8, 0.3, 0.2, 0.7);\n `;\n }\n if (!props.left) {\n return css`\n opacity: 0;\n `;\n }\n return '';\n }};\n`;\n\nconst getTabChildData = (\n mounted: boolean,\n ref: T | null,\n selected: TabValue,\n valueMap: Map,\n) => {\n const childIndex = valueMap.get(selected);\n if (!mounted || ref === null || childIndex === undefined) {\n return null;\n }\n const selectedChild = ref.children[childIndex];\n if (!selectedChild) {\n return null;\n }\n const tabDimensions = selectedChild.getBoundingClientRect();\n return (\n tabDimensions.left -\n ref.getBoundingClientRect().left +\n tabDimensions.width / 2\n );\n};\n\nconst Tabs: React.FunctionComponent = (props) => {\n const { value, onChange, children: propsChildren } = props;\n const tabsContainerRef = useRef(null as null | HTMLDivElement);\n const [mounted, setMounted] = useState(false);\n const [indicatorStyle, setIndicatorStyle] = useState({} as IndicatorStyles);\n const tabValues: Map = new Map();\n\n useEffect(() => setMounted(true), []);\n\n const children = React.Children.map(propsChildren, (child, i) => {\n if (!React.isValidElement(child)) {\n return null;\n }\n const childValue =\n child.props.value === undefined || child.props.value === null\n ? i\n : child.props.value;\n const selected = childValue === props.value;\n tabValues.set(childValue, i);\n\n return React.cloneElement(child, {\n selected: selected || undefined,\n value: childValue,\n onClick: (e: MouseEvent) => {\n onChange(childValue);\n if (child.props.onClick) {\n child.props.onClick(e);\n }\n },\n });\n });\n\n const tabsData = getTabChildData(\n mounted,\n tabsContainerRef.current,\n value,\n tabValues,\n );\n const updateIndicatorState = () => {\n if (tabsData === null) {\n return;\n }\n setIndicatorStyle({\n animation: generateAnimation(indicatorStyle.left || null, tabsData),\n left: tabsData,\n animationLength: indicatorStyle.left\n ? (Math.abs(indicatorStyle.left - tabsData) /\n ANIMATION_PIXEL_PER_SECOND) *\n 1000\n : undefined,\n });\n };\n\n useEffect(() => updateIndicatorState(), [tabsData]);\n\n return (\n \n {children}\n \n \n );\n};\n\nexport default Tabs;\n","// eslint-disable-next-line import/prefer-default-export\nexport const mathClamp = (min: number, max: number, value: number) =>\n Math.max(Math.min(value, max), min);\n","import * as React from 'react';\nimport styled from 'styled-components';\nimport theme from '../../theme';\n\ninterface Props extends React.ButtonHTMLAttributes {\n selected?: boolean;\n}\n\nconst Container = styled.button`\n font-family: Montserrat, sans-serif;\n font-size: 16px;\n color: ${theme.neutral.n200.hex()};\n display: inline-flex;\n background-color: transparent;\n margin: 0;\n padding: 12px 0;\n border: 0;\n outline: none;\n cursor: pointer;\n\n &:not(:last-child) {\n margin-right: 16px;\n }\n\n &:hover {\n color: ${theme.neutral.n100.hex()};\n }\n\n &[aria-selected='true'] {\n color: ${theme.accent.a500.hex()};\n cursor: default;\n }\n`;\n\nconst TabLink: React.FunctionComponent = (props) => {\n const { selected, children, disabled, ...rest } = props;\n return (\n \n {children}\n \n );\n};\n\nexport default TabLink;\n","import Tabs from './Tabs';\n\nexport * from './TabLink';\nexport default Tabs;\n","export const GET_STATUSES_START = 'GET_STATUSES_START';\nexport const GET_STATUSES_END = 'GET_STATUSES_END';\nexport const GET_STATUSES_ERROR = 'GET_STATUSES_ERROR';\n\nexport const GET_MOVIES_START = 'GET_MOVIES_START';\nexport const GET_MOVIES_END = 'GET_MOVIES_END';\nexport const GET_MOVIES_ERROR = 'GET_MOVIES_ERROR';\n\nexport const ADD_REACTION_START = 'ADD_REACTION_START';\nexport const ADD_REACTION_END = 'ADD_REACTION_END';\nexport const ADD_REACTION_ERROR = 'ADD_REACTION_ERROR';\n\nexport const UPDATE_MOVIE_START = 'UPDATE_MOVIE_START';\nexport const UPDATE_MOVIE_END = 'UPDATE_MOVIE_END';\nexport const UPDATE_MOVIE_ERROR = 'UPDATE_MOVIE_ERROR';\n\nexport const SET_MOVIE_MODAL = 'SET_MOVIE_MODAL';\nexport const SET_STATUS = 'SET_STATUS';\nexport const SET_MOVIE_NAME = 'SET_MOVIE_NAME';\n","import { MovieResponse } from '../../../server/shared/movie';\nimport { StatusResponse } from '../../../server/shared/status';\nimport { MoviesActions } from './actions';\nimport * as at from './actionTypes';\n\ninterface MoviesState {\n getStatusesLoading: boolean;\n statuses: StatusResponse[];\n getStatusesError: string | null;\n getMoviesLoading: boolean;\n movies: MovieResponse[];\n getMoviesError: string | null;\n addReactionLoading: boolean;\n addReactionError: string | null;\n updateMovieLoading: boolean;\n updateMovieError: string | null;\n selectedStatus: StatusResponse | null;\n movieModal: MovieResponse | null;\n page: {\n currentPage: number;\n pageSize: number;\n order: 'ASC' | 'DESC';\n totalCount: number;\n pageCount: number;\n };\n}\n\nexport const moviesDefaultState: MoviesState = {\n getStatusesLoading: false,\n statuses: [],\n getStatusesError: null,\n getMoviesLoading: false,\n movies: [],\n getMoviesError: null,\n addReactionLoading: false,\n addReactionError: null,\n updateMovieLoading: false,\n updateMovieError: null,\n selectedStatus: null,\n movieModal: null,\n page: {\n currentPage: 1,\n pageSize: 10,\n order: 'ASC',\n totalCount: 0,\n pageCount: 0,\n },\n};\n\nconst moviesReducer = (\n state: MoviesState,\n action: MoviesActions,\n): MoviesState => {\n switch (action.type) {\n case at.GET_STATUSES_START:\n return {\n ...state,\n getStatusesLoading: true,\n };\n case at.GET_STATUSES_END:\n return {\n ...state,\n getStatusesLoading: false,\n statuses: action.payload,\n getStatusesError: null,\n };\n case at.GET_STATUSES_ERROR:\n return {\n ...state,\n getStatusesLoading: false,\n statuses: [],\n getStatusesError: action.payload,\n };\n case at.GET_MOVIES_START:\n return {\n ...state,\n getMoviesLoading: true,\n };\n case at.GET_MOVIES_END:\n return {\n ...state,\n getMoviesLoading: false,\n movies: action.payload.data,\n getMoviesError: null,\n page: {\n currentPage: action.payload.currentPage,\n pageSize: action.payload.pageSize,\n order: action.payload.order,\n totalCount: action.payload.totalCount,\n pageCount: action.payload.pageCount,\n },\n };\n case at.GET_MOVIES_ERROR:\n return {\n ...state,\n getMoviesLoading: false,\n movies: [],\n getMoviesError: action.payload,\n };\n case at.ADD_REACTION_START:\n return {\n ...state,\n addReactionLoading: true,\n };\n case at.ADD_REACTION_END:\n return {\n ...state,\n addReactionLoading: false,\n addReactionError: null,\n };\n case at.ADD_REACTION_ERROR:\n return {\n ...state,\n addReactionLoading: false,\n addReactionError: action.payload,\n };\n case at.UPDATE_MOVIE_START:\n return {\n ...state,\n updateMovieLoading: true,\n };\n case at.UPDATE_MOVIE_END:\n return {\n ...state,\n updateMovieLoading: false,\n updateMovieError: null,\n };\n case at.UPDATE_MOVIE_ERROR:\n return {\n ...state,\n updateMovieLoading: false,\n updateMovieError: action.payload,\n };\n case at.SET_MOVIE_MODAL:\n return {\n ...state,\n movieModal: action.payload,\n };\n case at.SET_STATUS:\n return {\n ...state,\n selectedStatus: action.payload,\n };\n default:\n return state;\n }\n};\n\nexport default moviesReducer;\n","import { Dispatch } from 'react';\nimport actionCreator, { Actions } from '../../util/actionCreator';\nimport * as at from './actionTypes';\nimport {\n isErrorResponse,\n MoviesApi,\n StatusesApi,\n} from '../../../server/shared/client';\nimport { StatusResponse } from '../../../server/shared/status';\nimport { MovieResponse } from '../../../server/shared/movie';\nimport { EmojiType } from '../../components/Emoji';\nimport { PaginatedResponse } from '../../../server/shared/utility';\n\nexport const moviesActions = {\n getStatusesStart: () => actionCreator(at.GET_STATUSES_START),\n getStatusesEnd: (statuses: StatusResponse[]) =>\n actionCreator(at.GET_STATUSES_END, statuses),\n getStatusesError: (error: string) =>\n actionCreator(at.GET_STATUSES_ERROR, error),\n getMoviesStart: () => actionCreator(at.GET_MOVIES_START),\n getMoviesEnd: (movies: PaginatedResponse) =>\n actionCreator(at.GET_MOVIES_END, movies),\n getMoviesError: (error: string) => actionCreator(at.GET_MOVIES_ERROR, error),\n addReactionStart: () => actionCreator(at.ADD_REACTION_START),\n addReactionEnd: () => actionCreator(at.ADD_REACTION_END),\n addReactionError: (error: string) =>\n actionCreator(at.ADD_REACTION_ERROR, error),\n updateMovieStart: () => actionCreator(at.UPDATE_MOVIE_START),\n updateMovieEnd: () => actionCreator(at.UPDATE_MOVIE_END),\n updateMovieError: (error: string) =>\n actionCreator(at.UPDATE_MOVIE_ERROR, error),\n setMovieModal: (movie: MovieResponse | null) =>\n actionCreator(at.SET_MOVIE_MODAL, movie),\n setStatus: (status: StatusResponse) => actionCreator(at.SET_STATUS, status),\n setMovieName: (movieName: string) =>\n actionCreator(at.SET_MOVIE_NAME, movieName),\n};\n\nexport type MoviesActions = Actions;\n\nexport interface PaginationOptions {\n page: number;\n pageSize: number;\n order: 'ASC' | 'DESC';\n}\n\nexport const getMoviesByStatus = async (\n statusId: string,\n options: PaginationOptions,\n dispatch: Dispatch,\n) => {\n dispatch(moviesActions.getMoviesStart());\n const resp = await MoviesApi.getByStatus(statusId, options);\n\n if (isErrorResponse(resp)) {\n dispatch(moviesActions.getMoviesError('Could not fetch movies'));\n return;\n }\n dispatch(moviesActions.getMoviesEnd(resp));\n};\n\nexport const changeStatus = async (\n status: StatusResponse,\n options: Omit,\n dispatch: Dispatch,\n) => {\n dispatch(moviesActions.setStatus(status));\n await getMoviesByStatus(status.id, { ...options, page: 1 }, dispatch);\n};\n\nexport const getStatuses = async (\n options: PaginationOptions,\n dispatch: Dispatch,\n) => {\n dispatch(moviesActions.getStatusesStart());\n const resp = await StatusesApi.getAll();\n if (isErrorResponse(resp)) {\n dispatch(moviesActions.getStatusesError('Could not fetch statuses'));\n return;\n }\n const sortedStatuses = resp.sort((s1, s2) => s1.order - s2.order);\n dispatch(moviesActions.getStatusesEnd(sortedStatuses));\n\n if (sortedStatuses.length > 0) {\n await changeStatus(sortedStatuses[0], options, dispatch);\n }\n};\n\nexport const addReaction = async (\n reaction: EmojiType,\n movieId: string,\n selectedStatus: StatusResponse | null,\n options: PaginationOptions,\n dispatch: Dispatch,\n) => {\n dispatch(moviesActions.addReactionStart());\n const resp = await MoviesApi.updateReaction(movieId, { reaction });\n if (isErrorResponse(resp)) {\n dispatch(moviesActions.addReactionError('Could not add reaction'));\n return;\n }\n dispatch(moviesActions.addReactionEnd());\n\n if (selectedStatus) {\n await getMoviesByStatus(selectedStatus.id, options, dispatch);\n }\n};\n\nexport const markAsWatched = async (\n movieId: string,\n statuses: StatusResponse[],\n selectedStatus: StatusResponse | null,\n options: PaginationOptions,\n dispatch: Dispatch,\n) => {\n const targetStatus = statuses.find((s) => s.order === 2);\n if (!targetStatus) {\n return;\n }\n\n dispatch(moviesActions.updateMovieStart());\n const resp = await MoviesApi.update(movieId, { statusId: targetStatus.id });\n\n if (isErrorResponse(resp)) {\n dispatch(\n moviesActions.updateMovieError('Could not update the status to watched'),\n );\n return;\n }\n\n dispatch(moviesActions.updateMovieEnd());\n\n if (selectedStatus) {\n await getMoviesByStatus(selectedStatus.id, options, dispatch);\n }\n};\n","import actionCreator, { Actions } from '../util/actionCreator';\n\nconst OPEN_MODAL = 'OPEN_MODAL';\nconst CLOSE_MODAL = 'CLOSE_MODAL';\n\nconst uiActions = {\n openModal: (modalId: string) => actionCreator(OPEN_MODAL, modalId),\n closeModal: () => actionCreator(CLOSE_MODAL),\n};\n\nexport type UiActions = Actions;\n\nexport const openModal = (modalId: string) => uiActions.openModal(modalId);\nexport const closeModal = () => uiActions.closeModal();\n","import React, { useEffect } from 'react';\nimport ReactDOM from 'react-dom';\nimport styled, { css } from 'styled-components';\nimport { useAppDispatch, useAppSelector } from '../../hooks/redux';\nimport { selectModalOpen } from '../../selectors/uiSelectors';\nimport { closeModal, openModal } from '../../actions/uiActions';\nimport theme from '../../theme';\n\nconst modalRoot = document.getElementById('modal-root');\n\ninterface Props {\n show: boolean;\n modalId: string;\n verticalPosition?: 'top' | 'bottom' | 'center';\n allowClickThrough?: boolean;\n onBackdropClick?: React.HTMLAttributes['onClick'];\n}\n\ninterface ModalWrapperProps {\n allowClickThrough: boolean;\n}\n\nconst ModalWrapper = styled.div`\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 999;\n border: 5px solid ${theme.neutral.n900.hex()};\n pointer-events: ${({ allowClickThrough }) =>\n allowClickThrough ? 'none' : 'initial'};\n`;\n\nconst Backdrop = styled.div`\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n`;\n\ninterface ModalContentProps {\n verticalPosition: NonNullable;\n}\n\nconst ModalContent = styled.div`\n position: relative;\n pointer-events: none;\n display: flex;\n justify-content: center;\n width: 100%;\n height: 100%;\n\n ${({ verticalPosition }) => {\n if (verticalPosition === 'top') {\n return css``;\n }\n\n if (verticalPosition === 'bottom') {\n return css`\n align-items: flex-end;\n `;\n }\n\n return css`\n align-items: center;\n `;\n }}\n`;\n\nconst Modal: React.FunctionComponent = (props) => {\n const {\n show,\n modalId,\n children,\n onBackdropClick,\n allowClickThrough = false,\n verticalPosition = 'center',\n } = props;\n const dispatch = useAppDispatch();\n const modalOpen = useAppSelector(selectModalOpen);\n const clickThrough = !onBackdropClick && allowClickThrough;\n\n useEffect(() => {\n if (show && modalOpen !== modalId) {\n dispatch(openModal(modalId));\n } else if (!show && modalOpen === modalId) {\n dispatch(closeModal());\n }\n\n return () => {\n if (modalOpen === modalId) {\n dispatch(closeModal());\n }\n };\n }, [show]);\n\n if (modalOpen !== modalId) {\n return null;\n }\n\n const renderModal = () => {\n if (onBackdropClick) {\n return (\n \n \n \n {children}\n \n \n );\n }\n\n return (\n \n \n {children}\n \n \n );\n };\n\n return ReactDOM.createPortal(renderModal(), modalRoot!);\n};\n\nexport default Modal;\n","import Modal from './Modal';\n\nexport default Modal;\n","import React from 'react';\nimport styled from 'styled-components';\nimport Emoji, { EmojiType } from '../../../components/Emoji';\nimport theme from '../../../theme';\nimport Modal from '../../../components/Modal';\nimport { MovieResponse } from '../../../../server/shared/movie';\nimport getAvailableReactions from '../../../util/getAvailableReactions';\n\ninterface Props {\n movie: MovieResponse | null;\n onBackdropClick: NonNullable['onClick']>;\n onReaction: (r: EmojiType) => void;\n}\n\nconst Wrapper = styled.div`\n background-color: ${theme.neutral.black.hex()};\n padding: 12px;\n border-radius: 4px;\n justify-content: center;\n pointer-events: initial;\n border: 1px solid ${theme.tertiary.t500.hex()};\n margin: 0 4px 48px 4px;\n max-width: 100%;\n overflow: hidden;\n`;\n\nconst Title = styled.h3`\n font-size: 16px;\n margin-top: 0;\n color: ${theme.neutral.n100.hex()};\n text-align: center;\n`;\n\nconst ButtonContainer = styled.div`\n display: flex;\n padding-bottom: 8px;\n overflow: auto;\n`;\n\nconst ReactButton = styled.button`\n border: 0;\n display: flex;\n padding: 4px;\n background-color: ${theme.neutral.n900.hex()};\n cursor: pointer;\n border-radius: 4px;\n z-index: 2;\n\n &:disabled {\n background-color: ${theme.accent.a500.hex()};\n cursor: default;\n }\n\n &:not(:last-child) {\n margin-right: 12px;\n }\n`;\n\nconst modalTitle = (movie: Props['movie']) => {\n if (!movie) {\n return null;\n }\n\n if (movie.status.order === 0) {\n return (\n \n Approve <em>{movie.name}</em>?\n \n );\n }\n\n if (movie.status.order === 1) {\n return (\n \n Mark <em>{movie.name}</em> as watched?\n \n );\n }\n\n return (\n \n What did you think of <em>{movie.name}</em>?\n \n );\n};\n\nconst ReactionModal = (props: Props) => {\n const { movie, onBackdropClick, onReaction } = props;\n\n return (\n \n \n {modalTitle(movie)}\n \n {(movie ? getAvailableReactions(movie) : []).map((r) => (\n onReaction(r)}\n >\n \n \n ))}\n \n \n \n );\n};\n\nexport default ReactionModal;\n","import { RootState } from '../store';\n\nexport const selectLastMovieAdded = (state: RootState) =>\n state.movie.lastMovieAdded;\nexport const selectAddMovieLoading = (state: RootState) =>\n state.movie.addMovieLoading;\nexport const selectAddMovieError = (state: RootState) =>\n state.movie.addMovieError;\n","import React from 'react';\nimport styled from 'styled-components';\nimport theme from '../../theme';\nimport Icon from '../Icon';\n\nconst MAX_PAGES = 7;\n\ninterface Props {\n currentPage: number;\n pageSize: number;\n totalPages: number;\n onPageClick: (page: number, pageSize: number) => void;\n}\n\nconst Wrapper = styled.div``;\n\nconst Pages = styled.div`\n display: flex;\n`;\n\nconst PagePlaceholder = styled.p`\n margin: 0 8px 0 0;\n color: ${theme.primary.p500.hex()};\n background-color: transparent;\n border: 0;\n padding: 4px;\n height: 28px;\n min-width: 28px;\n border-radius: 4px;\n`;\n\nconst PageButton = styled.button`\n color: ${theme.primary.p500.hex()};\n background-color: transparent;\n cursor: pointer;\n border: 0;\n padding: 4px;\n height: 28px;\n min-width: 28px;\n margin-right: 8px;\n border-radius: 4px;\n\n &:disabled {\n border: 1px solid ${theme.primary.p600.hex()};\n cursor: default;\n }\n\n &:last-child {\n margin-right: 0;\n }\n`;\n\nconst ArrowButtons = styled(PageButton)`\n svg {\n fill: ${theme.primary.p500.hex()};\n }\n\n &:disabled {\n border: 0;\n\n svg {\n fill: ${theme.primary.p600.hex()};\n }\n }\n`;\n\nconst generatePageButton = (\n currentPage: number,\n page: number,\n pageSize: number,\n callback: Props['onPageClick'],\n) => (\n callback(page, pageSize)}\n disabled={currentPage === page}\n >\n {page}\n \n);\n\nconst generatePageButtons = (\n currentPage: number,\n totalPages: number,\n pageSize: number,\n callback: Props['onPageClick'],\n) => {\n if (totalPages <= MAX_PAGES) {\n return [...Array(totalPages)].map((_, i) =>\n generatePageButton(currentPage, i + 1, pageSize, callback),\n );\n }\n\n if (currentPage < MAX_PAGES - 2) {\n const startingPages = [...Array(MAX_PAGES - 2)].map((_, i) =>\n generatePageButton(currentPage, i + 1, pageSize, callback),\n );\n return [\n ...startingPages,\n ...,\n generatePageButton(currentPage, totalPages, pageSize, callback),\n ];\n }\n\n if (currentPage + MAX_PAGES - 4 >= totalPages) {\n const endingPages = [...Array(MAX_PAGES - 2)]\n .map((_, i) =>\n generatePageButton(currentPage, totalPages - i, pageSize, callback),\n )\n .reverse();\n return [\n generatePageButton(currentPage, 1, pageSize, callback),\n ...,\n ...endingPages,\n ];\n }\n\n return [\n generatePageButton(currentPage, 1, pageSize, callback),\n ...,\n generatePageButton(currentPage, currentPage - 1, pageSize, callback),\n generatePageButton(currentPage, currentPage, pageSize, callback),\n generatePageButton(currentPage, currentPage + 1, pageSize, callback),\n ...,\n generatePageButton(currentPage, totalPages, pageSize, callback),\n ];\n};\n\nconst Paginator = (props: Props) => {\n const { currentPage, pageSize, totalPages, onPageClick } = props;\n return (\n \n \n onPageClick(currentPage - 1, pageSize)}\n disabled={currentPage <= 1}\n >\n \n \n\n {generatePageButtons(currentPage, totalPages, pageSize, onPageClick)}\n\n onPageClick(currentPage + 1, pageSize)}\n disabled={currentPage >= totalPages}\n >\n \n \n \n \n );\n};\n\nexport default Paginator;\n","import Paginator from './Paginator';\n\nexport default Paginator;\n","import React, { useEffect, useReducer } from 'react';\nimport styled from 'styled-components';\nimport theme from '../../theme';\nimport MovieCard from '../../components/MovieCard/MovieCard';\nimport Tabs from '../../components/Tabs';\nimport TabLink from '../../components/Tabs/TabLink';\nimport moviesReducer, { moviesDefaultState } from './reducer';\nimport {\n addReaction,\n changeStatus,\n getMoviesByStatus,\n getStatuses,\n markAsWatched,\n moviesActions,\n PaginationOptions,\n} from './actions';\nimport ReactionModal from './components/ReactionModal';\nimport { EmojiType } from '../../components/Emoji';\nimport { useAppSelector } from '../../hooks/redux';\nimport { selectLastMovieAdded } from '../../selectors/movieSelectors';\nimport Paginator from '../../components/Paginator';\n\nconst Wrapper = styled.div`\n display: flex;\n flex-direction: column;\n max-height: 100%;\n background-color: ${theme.neutral.n900.hex()};\n color: white;\n font-size: 16px;\n`;\n\nconst Statuses = styled.div`\n display: flex;\n justify-content: center;\n margin-bottom: 24px;\n`;\n\nconst MovieList = styled.div`\n flex-grow: 1;\n overflow: hidden;\n`;\n\nconst MovieListScroll = styled.div`\n max-height: 100%;\n overflow: auto;\n display: flex;\n align-items: center;\n flex-direction: column;\n padding: 0 12px 24px 12px;\n`;\n\nconst PageInfo = styled.p`\n font-size: 14px;\n color: ${theme.neutral.n400.hex()};\n text-align: right;\n max-width: 500px;\n width: 100%;\n margin-top: 0;\n`;\n\nconst renderPageInfo = (\n page: number,\n pageSize: number,\n totalItems: number,\n pageCount: number,\n) => {\n if (pageCount <= 0) {\n return Showing 0 of 0;\n }\n\n const start = pageSize * (page - 1) + 1;\n const end = page === pageCount ? totalItems : start + pageSize - 1;\n return (\n \n Showing {start} - {end} of {totalItems}\n \n );\n};\n\nconst renderPaginator = (\n currentPage: number,\n pageSize: number,\n totalPages: number,\n callback: (page: number, itemsPerPage: number) => void,\n) => {\n if (totalPages <= 0) {\n return null;\n }\n\n return (\n \n );\n};\n\nconst Movies = () => {\n const [state, dispatch] = useReducer(moviesReducer, moviesDefaultState);\n const lastMovieAdded = useAppSelector(selectLastMovieAdded);\n const pageOptions: PaginationOptions = {\n order: 'ASC',\n page: state.page.currentPage,\n pageSize: state.page.pageSize,\n };\n\n useEffect(() => {\n getStatuses(pageOptions, dispatch);\n }, []);\n\n useEffect(() => {\n if (lastMovieAdded && state.statuses.length <= 0) {\n getStatuses(pageOptions, dispatch);\n return;\n }\n\n if (\n lastMovieAdded &&\n state.selectedStatus &&\n state.selectedStatus.order === 0\n ) {\n getMoviesByStatus(state.selectedStatus.id, pageOptions, dispatch);\n }\n }, [lastMovieAdded]);\n\n const setStatusById = async (id: string) => {\n const targetStatus = state.statuses.find((s) => s.id === id);\n if (\n (state.selectedStatus && state.selectedStatus.id === id) ||\n !targetStatus\n ) {\n return;\n }\n await changeStatus(\n targetStatus,\n { pageSize: pageOptions.pageSize, order: pageOptions.order },\n dispatch,\n );\n };\n\n const renderStatusSelector = () => {\n if (!state.selectedStatus) {\n return null;\n }\n\n return (\n \n setStatusById(v as string)}\n >\n {state.statuses.map((s) => (\n \n {s.displayName}\n \n ))}\n \n \n );\n };\n\n const handleReaction = (r: EmojiType) => {\n if (state.movieModal === null) {\n return;\n }\n const movieStatus = state.movieModal.status.order;\n const movieId = state.movieModal.id;\n\n if (movieStatus === 0 || movieStatus === 2) {\n addReaction(r, movieId, state.selectedStatus, pageOptions, dispatch);\n }\n\n if (movieStatus === 1 && r === 'checkMark') {\n markAsWatched(\n movieId,\n state.statuses,\n state.selectedStatus,\n pageOptions,\n dispatch,\n );\n }\n dispatch(moviesActions.setMovieModal(null));\n };\n\n const onPageEvent = (page: number, pageSize: number) => {\n if (state.selectedStatus) {\n getMoviesByStatus(\n state.selectedStatus.id,\n { page, pageSize, order: 'ASC' },\n dispatch,\n );\n }\n };\n\n return (\n \n {renderStatusSelector()}\n \n \n {renderPageInfo(\n state.page.currentPage,\n state.page.pageSize,\n state.page.totalCount,\n state.page.pageCount,\n )}\n {state.movies.map((m) => (\n \n dispatch(moviesActions.setMovieModal(m))\n }\n movie={m}\n key={m.id}\n />\n ))}\n {renderPaginator(\n state.page.currentPage,\n state.page.pageSize,\n state.page.pageCount,\n onPageEvent,\n )}\n \n \n dispatch(moviesActions.setMovieModal(null))}\n movie={state.movieModal}\n onReaction={handleReaction}\n />\n \n );\n};\n\nexport default Movies;\n","import Movies from './Movies';\n\nexport default Movies;\n","import React, { ChangeEvent, forwardRef } from 'react';\nimport styled from 'styled-components';\nimport theme, { colorStates, rgba } from '../../theme';\n\ninterface Props\n extends Omit, 'onChange'> {\n label: string;\n name: string;\n error?: string | null;\n onChange?: (value: string) => void;\n}\n\nconst Container = styled.div`\n display: flex;\n flex-direction: column-reverse;\n max-width: 350px;\n width: 100%;\n margin-bottom: 8px;\n text-align: left;\n position: relative;\n`;\n\nconst Label = styled.label`\n font-size: 12px;\n text-transform: capitalize;\n padding-bottom: 4px;\n font-weight: 600;\n transition: color 150ms ease;\n`;\n\ninterface InputProps {\n hasError: boolean;\n}\n\nconst StyledInput = styled.input`\n border: 1px solid\n ${(props) =>\n props.hasError\n ? colorStates.danger.default.hex()\n : theme.neutral.n600.hex()};\n border-radius: 6px;\n font-size: 16px;\n padding: 4px 8px;\n outline: none;\n background-color: transparent;\n color: ${theme.neutral.n100.hex()};\n width: 100%;\n margin-bottom: 20px;\n transition: background-color 150ms ease, border-color 150ms ease,\n color 150ms ease;\n\n &:hover,\n &:focus {\n box-shadow: 0 0 11px 1px ${theme.neutral.black.hex()};\n }\n\n &:focus {\n background-color: ${({ hasError }) =>\n hasError ? rgba(colorStates.danger.default, 0.1) : 'transparent'};\n\n & + ${Label} {\n color: ${theme.neutral.n100.hex()};\n }\n }\n\n & + ${Label} {\n color: ${({ hasError }) =>\n hasError\n ? `${colorStates.danger.default.hex()} !important`\n : theme.neutral.n300.hex()};\n }\n\n &:disabled {\n background-color: ${theme.neutral.n900.hex()};\n border-color: ${theme.neutral.n900.hex()};\n color: ${theme.neutral.n500.hex()};\n box-shadow: none;\n\n & + ${Label} {\n color: ${theme.neutral.n700.hex()};\n }\n }\n`;\n\nconst ErrorMessage = styled.p`\n color: ${colorStates.danger.default.hex()};\n font-size: 12px;\n margin: 0;\n position: absolute;\n bottom: 0;\n`;\n\nconst renderMessage = (error: Props['error']) => {\n if (!error) {\n return null;\n }\n\n return {error};\n};\n\nconst Input = forwardRef((props, ref) => {\n const {\n name,\n label,\n error,\n onChange: propsOnChange,\n type: inputType = 'text',\n ...rest\n } = props;\n\n const onChange = (e: ChangeEvent) => {\n if (propsOnChange) {\n propsOnChange(e.target.value);\n }\n };\n\n return (\n \n {renderMessage(error)}\n \n \n \n );\n});\n\nexport default Input;\n","import Input from './Input';\n\nexport default Input;\n","import { MovieResponse } from '../../server/shared/movie';\nimport actionCreator, { Actions } from '../util/actionCreator';\nimport { AppThunk } from '../store';\nimport { isErrorResponse, MoviesApi } from '../../server/shared/client';\n\nconst ADD_MOVIE_START = 'ADD_MOVIE_START';\nconst ADD_MOVIE_END = 'ADD_MOVIE_END';\nconst ADD_MOVIE_ERROR = 'ADD_MOVIE_ERROR';\n\nconst movieActions = {\n addMovieStart: () => actionCreator(ADD_MOVIE_START),\n addMovieEnd: (movie: MovieResponse) => actionCreator(ADD_MOVIE_END, movie),\n addMovieError: (error: string) => actionCreator(ADD_MOVIE_ERROR, error),\n};\n\nexport type MovieActions = Actions;\n\nexport const addMovie =\n (movieName: string): AppThunk =>\n async (dispatch) => {\n if (!movieName) {\n dispatch(movieActions.addMovieError('Please enter a movie name'));\n return;\n }\n\n dispatch(movieActions.addMovieStart());\n const response = await MoviesApi.create({ name: movieName });\n if (isErrorResponse(response)) {\n dispatch(movieActions.addMovieError('Could not add movie at this time'));\n return;\n }\n dispatch(movieActions.addMovieEnd(response));\n };\n","import React, { useEffect, useState } from 'react';\nimport styled from 'styled-components';\nimport Modal from '../Modal';\nimport Input from '../Input';\nimport theme from '../../theme';\nimport { useAppDispatch, useAppSelector } from '../../hooks/redux';\nimport { addMovie } from '../../actions/movieActions';\nimport {\n selectAddMovieError,\n selectAddMovieLoading,\n selectLastMovieAdded,\n} from '../../selectors/movieSelectors';\n\ninterface Props {\n show: boolean;\n onBackdropClick: NonNullable['onClick']>;\n}\n\nconst Wrapper = styled.form`\n pointer-events: initial;\n padding: 12px;\n\n input {\n &:focus {\n background-color: ${theme.neutral.black.hex()};\n }\n }\n`;\n\nconst NewMovieOverlay = (props: Props) => {\n const { show, onBackdropClick } = props;\n const [movieName, setMovieName] = useState('');\n const reduxDispatch = useAppDispatch();\n const addMovieLoading = useAppSelector(selectAddMovieLoading);\n const addMovieError = useAppSelector(selectAddMovieError);\n const lastMovieAdded = useAppSelector(selectLastMovieAdded);\n\n useEffect(() => {\n if (lastMovieAdded && !addMovieError && !addMovieLoading) {\n setMovieName('');\n }\n }, [lastMovieAdded, addMovieError, addMovieLoading]);\n\n const onSubmit: React.FormEventHandler = async (e) => {\n e.preventDefault();\n if (!addMovieLoading) {\n reduxDispatch(addMovie(movieName));\n }\n };\n\n return (\n \n \n setMovieName(v)}\n value={movieName}\n error={addMovieError}\n />\n \n \n );\n};\n\nexport default NewMovieOverlay;\n","import NewMovieOverlay from './NewMovieOverlay';\n\nexport default NewMovieOverlay;\n","import React, { useState } from 'react';\nimport { NavLink as Link } from 'react-router-dom';\nimport styled from 'styled-components';\nimport Icon from '../Icon';\nimport theme, { rgba } from '../../theme';\nimport NewMovieOverlay from '../NewMovieOverlay';\n\nconst Wrapper = styled.nav`\n display: flex;\n justify-content: space-between;\n background-color: ${theme.neutral.black.hex()};\n padding: 8px 24px;\n position: relative;\n`;\n\nconst NavLink = styled(Link)`\n display: flex;\n border-radius: 4px;\n padding: 8px;\n align-items: center;\n text-decoration: none;\n transition: background-color 150ms ease;\n justify-content: center;\n\n p {\n font-size: 12px;\n flex: 0;\n transform-origin: left;\n overflow: hidden;\n margin: 0;\n color: ${theme.primary.p500.hex()};\n font-weight: 600;\n transition: flex 150ms ease;\n }\n\n svg {\n fill: ${theme.neutral.white.hex()};\n }\n\n &.bottom-nav__link--active {\n p {\n margin-left: 4px;\n flex: 1;\n }\n }\n\n &:hover,\n &.bottom-nav__link--active {\n background-color: ${rgba(theme.primary.p500, 0.1)};\n\n svg {\n fill: ${theme.primary.p500.hex()};\n }\n }\n`;\n\nconst Fab = styled.button`\n display: flex;\n justify-content: center;\n position: absolute;\n padding: 4px;\n border: 0;\n border-radius: 50%;\n left: calc(50% - 18px);\n bottom: 40%;\n background: linear-gradient(\n 72deg,\n ${theme.tertiary.t500.hex()} 0%,\n ${theme.accent.a500.hex()} 40%,\n ${theme.primary.p500.hex()} 100%\n );\n cursor: pointer;\n\n svg {\n fill: ${theme.neutral.black.hex()};\n }\n`;\n\nconst BottomNav = () => {\n const [modalOpen, setModalOpen] = useState(false);\n\n return (\n \n \n \n

Movies

\n
\n setModalOpen(true)}>\n \n \n \n \n

Search

\n
\n setModalOpen(false)}\n />\n
\n );\n};\n\nexport default BottomNav;\n","import BottomNav from './BottomNav';\n\nexport default BottomNav;\n","export const SEARCH_MOVIES_START = 'SEARCH_MOVIES_START';\nexport const SEARCH_MOVIES_END = 'SEARCH_MOVIES_END';\nexport const SEARCH_MOVIES_ERROR = 'SEARCH_MOVIES_ERROR';\n\nexport const GET_STATUSES_START = 'GET_STATUSES_START';\nexport const GET_STATUSES_END = 'GET_STATUSES_END';\nexport const GET_STATUSES_ERROR = 'GET_STATUSES_ERROR';\n\nexport const ADD_REACTION_START = 'ADD_REACTION_START';\nexport const ADD_REACTION_END = 'ADD_REACTION_END';\nexport const ADD_REACTION_ERROR = 'ADD_REACTION_ERROR';\n\nexport const UPDATE_MOVIE_START = 'UPDATE_MOVIE_START';\nexport const UPDATE_MOVIE_END = 'UPDATE_MOVIE_END';\nexport const UPDATE_MOVIE_ERROR = 'UPDATE_MOVIE_ERROR';\n\nexport const SET_MOVIE_MODAL = 'SET_MOVIE_MODAL';\nexport const SET_QUERY = 'SET_QUERY';\n","import { Dispatch } from 'react';\nimport * as at from './actionTypes';\nimport actionCreator, { Actions } from '../../util/actionCreator';\nimport { MovieResponse } from '../../../server/shared/movie';\nimport {\n isErrorResponse,\n MoviesApi,\n StatusesApi,\n} from '../../../server/shared/client';\nimport { StatusResponse } from '../../../server/shared/status';\nimport { EmojiType } from '../../components/Emoji';\n\nexport interface MoviesWithStatus {\n status: string;\n order: number;\n movies: MovieResponse[];\n}\n\nexport const searchMoviesActions = {\n searchMoviesStart: () => actionCreator(at.SEARCH_MOVIES_START),\n searchMoviesEnd: (movies: MoviesWithStatus[]) =>\n actionCreator(at.SEARCH_MOVIES_END, movies),\n searchMoviesError: (error: string) =>\n actionCreator(at.SEARCH_MOVIES_ERROR, error),\n getStatusesStart: () => actionCreator(at.GET_STATUSES_START),\n getStatusesEnd: (statuses: StatusResponse[]) =>\n actionCreator(at.GET_STATUSES_END, statuses),\n getStatusesError: (error: string) =>\n actionCreator(at.GET_STATUSES_ERROR, error),\n addReactionStart: () => actionCreator(at.ADD_REACTION_START),\n addReactionEnd: () => actionCreator(at.ADD_REACTION_END),\n addReactionError: (error: string) =>\n actionCreator(at.ADD_REACTION_ERROR, error),\n updateMovieStart: () => actionCreator(at.UPDATE_MOVIE_START),\n updateMovieEnd: () => actionCreator(at.UPDATE_MOVIE_END),\n updateMovieError: (error: string) =>\n actionCreator(at.UPDATE_MOVIE_ERROR, error),\n setMovieModal: (movie: MovieResponse | null) =>\n actionCreator(at.SET_MOVIE_MODAL, movie),\n setQuery: (query: string) => actionCreator(at.SET_QUERY, query),\n};\n\nexport type SearchMoviesActions = Actions;\n\nexport const searchMovies = async (\n query: string,\n dispatch: Dispatch,\n) => {\n if (query.length < 3) {\n dispatch(\n searchMoviesActions.searchMoviesError(\n 'Please enter more than 3 characters',\n ),\n );\n return;\n }\n\n dispatch(searchMoviesActions.searchMoviesStart());\n\n const response = await MoviesApi.search(query);\n\n if (isErrorResponse(response)) {\n dispatch(\n searchMoviesActions.searchMoviesError(\n 'Could not find the requested movie',\n ),\n );\n return;\n }\n\n const moviesWithStatus: Record = response.reduce(\n (sorted, m) => {\n const existing = sorted[m.status.displayName];\n\n if (existing) {\n return {\n ...sorted,\n [m.status.displayName]: {\n ...existing,\n movies: [...existing.movies, m],\n },\n };\n }\n return {\n ...sorted,\n [m.status.displayName]: {\n status: m.status.displayName,\n order: m.status.order,\n movies: [m],\n },\n };\n },\n {} as Record,\n );\n\n const movies = Object.keys(moviesWithStatus)\n .map((k) => moviesWithStatus[k])\n .sort((a, b) => a.order - b.order);\n\n dispatch(searchMoviesActions.searchMoviesEnd(movies));\n};\n\nexport const getStatuses = async (dispatch: Dispatch) => {\n dispatch(searchMoviesActions.getStatusesStart());\n const resp = await StatusesApi.getAll();\n if (isErrorResponse(resp)) {\n dispatch(searchMoviesActions.getStatusesError('Could not fetch statuses'));\n return;\n }\n dispatch(searchMoviesActions.getStatusesEnd(resp));\n};\n\nexport const markAsWatched = async (\n movieId: string,\n query: string,\n statuses: StatusResponse[],\n dispatch: Dispatch,\n) => {\n const targetStatus = statuses.find((s) => s.order === 2);\n if (!targetStatus) {\n return;\n }\n\n dispatch(searchMoviesActions.updateMovieStart());\n const resp = await MoviesApi.update(movieId, { statusId: targetStatus.id });\n\n if (isErrorResponse(resp)) {\n dispatch(\n searchMoviesActions.updateMovieError(\n 'Could not update the status to watched',\n ),\n );\n return;\n }\n\n dispatch(searchMoviesActions.updateMovieEnd());\n\n if (query) {\n await searchMovies(query, dispatch);\n }\n};\n\nexport const addReaction = async (\n reaction: EmojiType,\n movieId: string,\n query: string,\n dispatch: Dispatch,\n) => {\n dispatch(searchMoviesActions.addReactionStart());\n const resp = await MoviesApi.updateReaction(movieId, { reaction });\n if (isErrorResponse(resp)) {\n dispatch(searchMoviesActions.addReactionError('Could not add reaction'));\n return;\n }\n dispatch(searchMoviesActions.addReactionEnd());\n\n if (query) {\n await searchMovies(query, dispatch);\n }\n};\n","import { MoviesWithStatus, SearchMoviesActions } from './actions';\nimport * as at from './actionTypes';\nimport { StatusResponse } from '../../../server/shared/status';\nimport { MovieResponse } from '../../../server/shared/movie';\n\ninterface SearchMovieState {\n query: string;\n searchMoviesLoading: boolean;\n movies: MoviesWithStatus[];\n searchMoviesError: string | null;\n getStatusesLoading: boolean;\n statuses: StatusResponse[];\n getStatusesError: string | null;\n addReactionLoading: boolean;\n addReactionError: string | null;\n updateMovieLoading: boolean;\n updateMovieError: string | null;\n movieModal: MovieResponse | null;\n}\n\nexport const defaultSearchMoviesState: SearchMovieState = {\n query: '',\n searchMoviesLoading: false,\n movies: [],\n searchMoviesError: null,\n getStatusesLoading: false,\n statuses: [],\n getStatusesError: null,\n addReactionLoading: false,\n addReactionError: null,\n updateMovieLoading: false,\n updateMovieError: null,\n movieModal: null,\n};\n\nexport const searchMoviesReducer = (\n state: SearchMovieState,\n action: SearchMoviesActions,\n): SearchMovieState => {\n switch (action.type) {\n case at.SEARCH_MOVIES_START:\n return {\n ...state,\n searchMoviesLoading: true,\n };\n case at.SEARCH_MOVIES_END:\n return {\n ...state,\n searchMoviesLoading: false,\n searchMoviesError: null,\n movies: action.payload,\n };\n case at.SEARCH_MOVIES_ERROR:\n return {\n ...state,\n searchMoviesLoading: false,\n searchMoviesError: action.payload,\n movies: [],\n };\n case at.SET_QUERY:\n return {\n ...state,\n query: action.payload,\n };\n case at.GET_STATUSES_START:\n return {\n ...state,\n getStatusesLoading: true,\n };\n case at.GET_STATUSES_END:\n return {\n ...state,\n getStatusesLoading: false,\n statuses: action.payload,\n getStatusesError: null,\n };\n case at.GET_STATUSES_ERROR:\n return {\n ...state,\n getStatusesLoading: false,\n statuses: [],\n getStatusesError: action.payload,\n };\n case at.ADD_REACTION_START:\n return {\n ...state,\n addReactionLoading: true,\n };\n case at.ADD_REACTION_END:\n return {\n ...state,\n addReactionLoading: false,\n addReactionError: null,\n };\n case at.ADD_REACTION_ERROR:\n return {\n ...state,\n addReactionLoading: false,\n addReactionError: action.payload,\n };\n case at.UPDATE_MOVIE_START:\n return {\n ...state,\n updateMovieLoading: true,\n };\n case at.UPDATE_MOVIE_END:\n return {\n ...state,\n updateMovieLoading: false,\n updateMovieError: null,\n };\n case at.UPDATE_MOVIE_ERROR:\n return {\n ...state,\n updateMovieLoading: false,\n updateMovieError: action.payload,\n };\n case at.SET_MOVIE_MODAL:\n return {\n ...state,\n movieModal: action.payload,\n };\n default:\n return state;\n }\n};\n","/**\n * Returns function that will only be called after the debounce time has elapsed. If\n * the function is repeteadly called before the time has elapsed, the timer will\n * reset.\n * @param {function} func Function to debounce\n * @param {number} debounceTime Time to wait, in milliseconds, before calling func\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst debounce = any>(\n func: T,\n debounceTime: number,\n) => {\n let timeout: ReturnType | null = null;\n return (...args: Parameters) => {\n const later = () => {\n timeout = null;\n func.apply({}, args);\n };\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(later, debounceTime);\n };\n};\n\nexport default debounce;\n","import MovieCard from './MovieCard';\n\nexport default MovieCard;\n","import React from 'react';\nimport styled from 'styled-components';\nimport { MoviesWithStatus } from '../actions';\nimport MovieCard from '../../../components/MovieCard';\nimport theme from '../../../theme';\nimport { MovieResponse } from '../../../../server/shared/movie';\n\ninterface Props {\n statusList: MoviesWithStatus;\n openMovieModal: (movie: MovieResponse) => void;\n}\n\nconst Wrapper = styled.div`\n width: 100%;\n\n &:not(:last-child) {\n margin-bottom: 12px;\n }\n\n h3 {\n font-size: 16px;\n }\n`;\n\nconst Title = styled.h3<{ statusColor: string }>`\n font-size: 16px;\n margin: 0 0 8px 0;\n padding-bottom: 4px;\n color: ${({ statusColor }) => statusColor};\n border-bottom: 1px solid ${({ statusColor }) => statusColor};\n`;\n\nconst getStatusColor = (status: string) => {\n switch (status) {\n case 'Suggested':\n return theme.red.r500.hex();\n case 'Pending':\n return theme.accent.a500.hex();\n case 'Watched':\n return theme.blue.b500.hex();\n default:\n return theme.neutral.white.hex();\n }\n};\n\nconst StatusMovieList = (props: Props) => {\n const { openMovieModal, statusList } = props;\n\n return (\n \n \n {statusList.status}\n \n {statusList.movies.map((m) => (\n openMovieModal(m)}\n movie={m}\n key={m.id}\n />\n ))}\n \n );\n};\n\nexport default StatusMovieList;\n","import React, { useEffect, useReducer } from 'react';\nimport styled from 'styled-components';\nimport {\n addReaction,\n getStatuses,\n markAsWatched,\n searchMovies,\n searchMoviesActions,\n} from './actions';\nimport { defaultSearchMoviesState, searchMoviesReducer } from './reducer';\nimport Input from '../../components/Input';\nimport theme from '../../theme';\nimport debounce from '../../util/debounce';\nimport StatusMovieList from './components/StatusMovieList';\nimport { EmojiType } from '../../components/Emoji';\nimport ReactionModal from '../Movies/components/ReactionModal';\nimport { MovieResponse } from '../../../server/shared/movie';\n\nconst Wrapper = styled.div`\n display: flex;\n flex-direction: column;\n height: 100%;\n max-height: 100%;\n background-color: ${theme.neutral.n900.hex()};\n color: white;\n font-size: 16px;\n`;\n\nconst InputWrapper = styled.div`\n display: flex;\n justify-content: center;\n width: 100%;\n margin: 8px 0;\n`;\n\nconst MovieList = styled.div`\n flex-grow: 1;\n overflow: hidden;\n`;\n\nconst MovieListScroll = styled.div`\n max-height: 100%;\n overflow: auto;\n display: flex;\n align-items: center;\n flex-direction: column;\n padding: 0 12px 24px 12px;\n`;\n\nconst debouncedSearch = debounce(searchMovies, 350);\n\nconst SearchMovies = () => {\n const [state, dispatch] = useReducer(\n searchMoviesReducer,\n defaultSearchMoviesState,\n );\n\n useEffect(() => {\n getStatuses(dispatch);\n }, []);\n\n useEffect(() => {\n if (state.query) {\n debouncedSearch(state.query, dispatch);\n }\n }, [state.query]);\n\n const handleReaction = (r: EmojiType) => {\n if (state.movieModal === null) {\n return;\n }\n const movieStatus = state.movieModal.status.order;\n const movieId = state.movieModal.id;\n\n if (movieStatus === 0 || movieStatus === 2) {\n addReaction(r, movieId, state.query, dispatch);\n }\n\n if (movieStatus === 1 && r === 'checkMark') {\n markAsWatched(movieId, state.query, state.statuses, dispatch);\n }\n dispatch(searchMoviesActions.setMovieModal(null));\n };\n\n const openMovieModal = (movie: MovieResponse) =>\n dispatch(searchMoviesActions.setMovieModal(movie));\n\n return (\n \n \n dispatch(searchMoviesActions.setQuery(v))}\n error={state.query.length > 0 ? state.searchMoviesError : null}\n />\n \n\n \n \n {state.movies.map((m) => (\n \n ))}\n \n \n \n dispatch(searchMoviesActions.setMovieModal(null))\n }\n movie={state.movieModal}\n onReaction={handleReaction}\n />\n \n );\n};\n\nexport default SearchMovies;\n","import SearchMovies from './SearchMovies';\n\nexport default SearchMovies;\n","import React, { useEffect, useRef, useState } from 'react';\nimport { Route, Switch } from 'react-router-dom';\nimport styled from 'styled-components';\nimport AuthRoute from '../components/AuthRoute';\nimport Movies from './Movies';\nimport BottomNav from '../components/BottomNav';\nimport SearchMovies from './SearchMovies';\n\nconst AUTH_ROUTES = {\n movies: '/movies',\n search: '/search',\n};\n\nconst ALL_ROUTES = (\n Object.keys(AUTH_ROUTES) as (keyof typeof AUTH_ROUTES)[]\n).map((k) => AUTH_ROUTES[k]);\n\nconst Wrapper = styled.div`\n display: flex;\n flex-direction: column;\n min-height: 100%;\n`;\n\nconst PageContent = styled.div<{ childHeight: number }>`\n flex-basis: 0;\n flex-grow: 1;\n overflow: hidden;\n\n .page-content-child {\n height: ${({ childHeight }) => childHeight}px;\n }\n`;\n\nconst AuthPages = () => {\n // TODO: Figure out why chome height is wrong with flexbox\n const pageContent = useRef(null);\n const contentHeight = pageContent.current?.clientHeight;\n const [currentHeight, setCurrentHeight] = useState(-1);\n\n useEffect(() => {\n if (contentHeight !== currentHeight) {\n setCurrentHeight(contentHeight || 0);\n }\n }, [contentHeight]);\n\n return (\n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n );\n};\n\nexport default AuthPages;\n","import React from 'react';\nimport { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom';\nimport styled, { css } from 'styled-components';\nimport ROUTES from './routes';\nimport Login from './pages/Login';\nimport { useAppSelector } from './hooks/redux';\nimport { selectModalOpen } from './selectors/uiSelectors';\nimport AuthPages from './pages/AuthPages';\n\nconst blurStyling = css`\n filter: blur(2px);\n`;\n\ninterface AppWrapperProps {\n modalOpen: boolean;\n}\n\nconst AppWrapper = styled.div`\n height: 100%;\n ${({ modalOpen }) => (modalOpen ? blurStyling : '')};\n`;\n\nconst App = () => {\n const modalOpen = useAppSelector(selectModalOpen);\n\n return (\n \n \n \n \n \n \n\n \n\n \n \n \n\n 404 not found \n \n \n \n );\n};\n\nexport default App;\n","import { ReportHandler } from 'web-vitals';\n\nconst reportWebVitals = (onPerfEntry?: ReportHandler) => {\n if (onPerfEntry && onPerfEntry instanceof Function) {\n import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\n getCLS(onPerfEntry);\n getFID(onPerfEntry);\n getFCP(onPerfEntry);\n getLCP(onPerfEntry);\n getTTFB(onPerfEntry);\n });\n }\n};\n\nexport default reportWebVitals;\n","import { TokenResponse } from '../../server/shared/token';\nimport { SessionActions } from '../actions/sessionActions';\nimport { UserResponse } from '../../server/shared/user';\n\ninterface SessionState {\n tokenLoading: boolean;\n tokenError: string | null;\n token: TokenResponse | null;\n userLoading: boolean;\n userError: string | null;\n user: UserResponse | null;\n}\n\nconst defaultSessionState: SessionState = {\n tokenLoading: false,\n tokenError: null,\n token: null,\n userLoading: false,\n userError: null,\n user: null,\n};\n\nconst sessionReducer = (\n state = defaultSessionState,\n action: SessionActions,\n): SessionState => {\n switch (action.type) {\n case 'GET_TOKEN_START':\n return {\n ...state,\n tokenLoading: true,\n token: null,\n };\n case 'GET_TOKEN_ERROR':\n return {\n ...state,\n tokenLoading: false,\n token: null,\n tokenError: action.payload,\n };\n case 'GET_TOKEN_END':\n return {\n ...state,\n tokenLoading: false,\n token: action.payload,\n tokenError: null,\n };\n case 'GET_CURRENT_USER_START':\n return {\n ...state,\n userLoading: true,\n user: null,\n };\n case 'GET_CURRENT_USER_END':\n return {\n ...state,\n userLoading: false,\n userError: null,\n user: action.payload,\n };\n case 'GET_CURRENT_USER_ERROR':\n return {\n ...state,\n userLoading: false,\n user: null,\n userError: action.payload,\n };\n default:\n return state;\n }\n};\n\nexport default sessionReducer;\n","import { UiActions } from '../actions/uiActions';\n\ninterface UiState {\n modalOpen: string | null;\n}\n\nconst defaultUiState: UiState = {\n modalOpen: null,\n};\n\nconst uiReducer = (state = defaultUiState, action: UiActions): UiState => {\n switch (action.type) {\n case 'OPEN_MODAL':\n return {\n ...state,\n modalOpen: action.payload,\n };\n case 'CLOSE_MODAL':\n return {\n ...state,\n modalOpen: null,\n };\n default:\n return state;\n }\n};\n\nexport default uiReducer;\n","import { MovieResponse } from '../../server/shared/movie';\nimport { MovieActions } from '../actions/movieActions';\n\ninterface MovieState {\n lastMovieAdded: MovieResponse | null;\n addMovieError: string | null;\n addMovieLoading: boolean;\n}\n\nconst defaultMovieState: MovieState = {\n lastMovieAdded: null,\n addMovieError: null,\n addMovieLoading: false,\n};\n\nconst movieReducer = (\n state = defaultMovieState,\n action: MovieActions,\n): MovieState => {\n switch (action.type) {\n case 'ADD_MOVIE_START':\n return {\n ...state,\n addMovieLoading: true,\n };\n case 'ADD_MOVIE_END':\n return {\n ...state,\n addMovieLoading: false,\n lastMovieAdded: action.payload,\n addMovieError: null,\n };\n case 'ADD_MOVIE_ERROR':\n return {\n ...state,\n addMovieLoading: false,\n addMovieError: action.payload,\n };\n default:\n return state;\n }\n};\n\nexport default movieReducer;\n","import { Action, configureStore, ThunkAction } from '@reduxjs/toolkit';\nimport thunk from 'redux-thunk';\nimport sessionReducer from './reducers/sessionReducer';\nimport uiReducer from './reducers/uiReducer';\nimport movieReducer from './reducers/movieReducer';\n\nexport const store = configureStore({\n reducer: {\n session: sessionReducer,\n ui: uiReducer,\n movie: movieReducer,\n },\n middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(thunk),\n devTools: process.env.NODE_ENV !== 'production',\n});\n\nexport type AppDispatch = typeof store.dispatch;\nexport type RootState = ReturnType;\nexport type AppThunk = ThunkAction<\n ReturnType,\n RootState,\n unknown,\n Action\n>;\n","import React, { Suspense } from 'react';\nimport ReactDOM from 'react-dom';\nimport { Provider } from 'react-redux';\nimport i18n from 'i18next';\nimport { initReactI18next } from 'react-i18next';\nimport Backend from 'i18next-xhr-backend';\nimport LanguageDetector from 'i18next-browser-languagedetector';\nimport './client/index.scss';\nimport App from './client/App';\nimport reportWebVitals from './client/reportWebVitals';\nimport { store } from './client/store';\n\ni18n\n .use(Backend)\n .use(LanguageDetector)\n .use(initReactI18next)\n .init({\n fallbackLng: 'en',\n debug: true,\n ns: ['main'],\n defaultNS: 'main',\n preload: ['en'],\n interpolation: {\n escapeValue: false,\n },\n });\n\nReactDOM.render(\n \n \n \n \n \n \n ,\n document.getElementById('root'),\n);\n\n// If you want to start measuring performance in your app, pass a function\n// to log results (for example: reportWebVitals(console.log))\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\nreportWebVitals();\n"],"sourceRoot":""}