\r\n {children({ data: state.currentPageData, params, page: state.page, total: totalNumItems, products: state.data })}\r\n \r\n \r\n {!hasItems && emptyComponent}\r\n {hasItems && t('slug:showing-1-to-post-of-total', { post: numItems.toString(), total: totalNumItems })}\r\n \r\n {hasItems && numItems < totalNumItems &&\r\n React.cloneElement(nextPageComponent, {\r\n className: _classes.showMoreButton,\r\n onClick: onNextPage\r\n })}\r\n
\r\n \r\n )\r\n}\r\n\r\nexport default Searchable\r\n","import { useState, useEffect } from 'react'\r\nimport { globalHistory } from '@reach/router'\r\nimport { splitQueryString } from 'tcweb-material-components/core/utilities/helpers'\r\n\r\nimport { getLocaleFromPath } from '../utilities/locale'\r\n\r\nconst useLocation = () => {\r\n const initialState = {\r\n oldLocation: null,\r\n location: globalHistory.location,\r\n transitioning: globalHistory.transitioning,\r\n locale: getLocaleFromPath(globalHistory.location.pathname) || 'en-us',\r\n querystring: splitQueryString(globalHistory.location.href)\r\n }\r\n\r\n const [state, setState] = useState(initialState)\r\n\r\n useEffect(() => {\r\n const removeListener = globalHistory.listen(params => {\r\n const { location, transitioning } = params\r\n\r\n setState({\r\n ...state,\r\n oldLocation: state.location,\r\n location,\r\n transitioning,\r\n locale: getLocaleFromPath(location.pathname) || 'en-us',\r\n querystring: splitQueryString(location.href)\r\n })\r\n })\r\n return removeListener\r\n }, [state])\r\n\r\n return state\r\n}\r\n\r\nexport default useLocation\r\n","import React, { useContext, useReducer, useEffect } from 'react'\r\nimport moment from 'moment'\r\nimport debounce from 'lodash/debounce'\r\nimport trim from 'lodash/trim'\r\nimport orderBy from 'lodash/orderBy'\r\nimport forEach from 'lodash/forEach'\r\nimport range from 'lodash/range'\r\nimport head from 'lodash/head'\r\nimport { graphql } from 'gatsby'\r\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome'\r\nimport { makeStyles, useTheme } from '@material-ui/core/styles'\r\nimport useMediaQuery from '@material-ui/core/useMediaQuery'\r\nimport Typography from '@material-ui/core/Typography'\r\nimport TextField from '@material-ui/core/TextField'\r\nimport InputAdornment from '@material-ui/core/InputAdornment'\r\nimport filter from 'tcweb-material-components/core/poly/filter'\r\nimport find from 'tcweb-material-components/core/poly/find'\r\nimport count from 'tcweb-material-components/core/poly/count'\r\nimport map from 'tcweb-material-components/core/poly/map'\r\nimport Button from 'tcweb-material-components/core/button'\r\nimport Section from 'tcweb-material-components/core/section'\r\n//import Tabs from 'tcweb-material-components/core/tabs'\r\n//import Tab from 'tcweb-material-components/core/tab'\r\nimport get from 'tcweb-material-components/core/poly/get'\r\nimport Helmet from 'react-helmet'\r\nimport { useTranslation } from 'react-i18next'\r\nimport loadable from '@loadable/component'\r\nimport { normalizePosts, normalizeGearsPosts } from '../utilities/helpers'\r\n\r\nimport { getLocaleFromWPMLLocale } from '../utilities/locale'\r\nimport Searchable from '../components/common/searchable'\r\nimport useLocation from '../hooks/useLocation'\r\nimport LocaleContext from '../components/context/localeContext'\r\nimport LayoutContent from '../components/layout/layoutContent'\r\n\r\nconst Skeleton = loadable(() => import('@material-ui/lab/Skeleton'))\r\nconst Grid = loadable(() => import('tcweb-material-components/core/grid'))\r\n\r\nconst SPECIAL_CHAR_REGEXP = /[^\\w\\s\\d]/\r\nconst INITIAL_POSTS_TO_SHOW = 5\r\nconst POSTS_TO_GET = 6\r\nconst NUM_DAYS_PER_WEEK = 7\r\n\r\nconst useStyles = makeStyles(({ breakpoints, palette, spacing, accessibility: { highContrast } }) => ({\r\n heading: {\r\n paddingTop: '30px',\r\n [breakpoints.up('md')]: {\r\n paddingTop: '100px'\r\n }\r\n },\r\n section: {\r\n position: 'relative',\r\n paddingBottom: '30px',\r\n [breakpoints.up('md')]: {\r\n paddingBottom: '100px'\r\n }\r\n },\r\n root: {\r\n paddingBottom: '3em',\r\n [breakpoints.up('md')]: {\r\n paddingTop: '4em',\r\n paddingBottom: '4em'\r\n }\r\n },\r\n searchable: {\r\n [breakpoints.up('md')]: {\r\n marginLeft: 'auto',\r\n marginRight: 'auto',\r\n width: '50vw'\r\n }\r\n },\r\n noPostsTitle: { margin: 0 },\r\n input: {\r\n display: 'flex',\r\n width: '100%',\r\n marginLeft: 'auto',\r\n marginRight: 'auto',\r\n [breakpoints.up('md')]: { maxWidth: '50vw' }\r\n },\r\n categoryTab: {\r\n '&:hover': {\r\n backgroundColor: palette.secondary.light,\r\n color: palette.text.primary\r\n }\r\n },\r\n searchTextField: {\r\n [breakpoints.up('md')]: {\r\n display: 'flex',\r\n marginLeft: 'auto',\r\n marginRight: 'auto',\r\n width: 'calc(100vw / 3)'\r\n },\r\n textTransform: 'uppercase',\r\n fontWeight: 'bold',\r\n fontStretch: 'condensed',\r\n letterSpacing: '.6px'\r\n },\r\n searchTextInput: {\r\n border: `1px solid ${palette.border.primary}`,\r\n borderRadius: 10,\r\n backgroundColor: palette.background.default,\r\n padding: spacing(1),\r\n '&:hover, &:focus': {\r\n [highContrast.mediaQuery('active')]: {\r\n border: `1px solid Highlight`\r\n }\r\n }\r\n },\r\n searchFocusVisible: {\r\n [highContrast.mediaQuery('active')]: {\r\n border: `1px solid Highlight`\r\n }\r\n },\r\n skeleton: { background: 'rgba(0, 0, 0, 0.2)' },\r\n searchIcon: {\r\n [highContrast.mediaQuery('active')]: {\r\n color: 'WindowText'\r\n }\r\n }\r\n}))\r\n\r\nconst getTranslation = (localeContext, post) =>\r\n localeContext.isImplicit ? post : find((t) => getLocaleFromWPMLLocale(t.locale.locale) === localeContext.locale, post.translated) || post\r\n\r\nconst getQueryRegExp = (query) =>\r\n new RegExp(\r\n String(query)\r\n .toLowerCase()\r\n .replace(SPECIAL_CHAR_REGEXP, '')\r\n .split(' ')\r\n .filter(Boolean)\r\n .map((i) => `(?=.*${i})`)\r\n .join(''),\r\n 'gm'\r\n )\r\n\r\nconst getCategories = (posts) => {\r\n const categories = new Set()\r\n\r\n forEach(posts, (post) => categories.add(post.node.wordpress_site))\r\n\r\n return [...categories].sort()\r\n}\r\n\r\nconst onSearch = (localeContext) => (data, params) => {\r\n let filteredPosts = [...data]\r\n\r\n if (params.category) {\r\n filteredPosts = filteredPosts.filter(post => post.node.wordpress_site === params.category)\r\n }\r\n\r\n if (trim(params.query)) {\r\n const query = getQueryRegExp(params.query)\r\n\r\n filteredPosts = filter((item) => {\r\n const post = getTranslation(localeContext, item.node)\r\n return (\r\n [post.title, ...map((tag) => tag.name, item.node.tags.nodes)].join(' ').toLowerCase().replace(SPECIAL_CHAR_REGEXP, '').search(query) >\r\n -1\r\n )\r\n }, filteredPosts)\r\n }\r\n\r\n return filteredPosts\r\n}\r\n\r\nconst reducer = (state, action) => {\r\n switch (action.type) {\r\n case 'set':\r\n return { ...state, ...action.payload }\r\n default:\r\n throw new Error(`Invalid action type passed to reducer: ${action.type}`)\r\n }\r\n}\r\n\r\nconst NewsCard = loadable(() => import('../components/common/newsCard'))\r\n\r\nconst NewsPage = (props) => {\r\n const theme = useTheme()\r\n const isDesktop = useMediaQuery(theme.breakpoints.up('lg'))\r\n const classes = useStyles()\r\n const { querystring } = useLocation()\r\n const { t } = useTranslation()\r\n const localeContext = useContext(LocaleContext)\r\n const { sitePosts, siteData, /*esportsPosts = [], gears5Posts = [], tacticsPosts = [] */} = props.data\r\n\r\n const normalizedGOWPosts = normalizeGearsPosts(sitePosts.edges, 'gears-of-war')\r\n /*const normalizedEsportsPosts = normalizePosts(esportsPosts.posts.edges, 'gears-esports')\r\n const normalizedGears5Posts = normalizePosts(gears5Posts.posts.edges, 'gears-5')\r\n const normalizedTacticsPosts = normalizePosts(tacticsPosts.posts.edges, 'gears-tactics')\r\n\r\n let normalizedPosts = [...normalizedEsportsPosts, ...normalizedGears5Posts, ...normalizedTacticsPosts].filter(\r\n (post) => post.node.wpml_current_locale === localeContext.locale\r\n )*/\r\n\r\n const allPosts = [...normalizedGOWPosts/*, ...normalizedPosts*/]\r\n\r\n const [state, dispatch] = useReducer(reducer, {\r\n query: querystring.query || '',\r\n category: querystring.category || '',\r\n categories: getCategories(allPosts),\r\n posts: orderBy(allPosts, 'node.date', 'desc')\r\n })\r\n\r\n useEffect(() => {\r\n const timeSpans = {\r\n s: 'second',\r\n ss: 'seconds',\r\n m: 'minute',\r\n mm: 'minutes',\r\n h: 'hour',\r\n hh: 'hours',\r\n d: 'day',\r\n dd: 'days',\r\n w: 'week',\r\n ww: 'weeks',\r\n M: 'month',\r\n MM: 'months',\r\n y: 'year',\r\n yy: 'years'\r\n }\r\n\r\n const _relativeTimeParser = (number, _, key) => {\r\n if ((key === 'dd' || key === 'd') && number >= NUM_DAYS_PER_WEEK) {\r\n const weeks = Math.floor(number / NUM_DAYS_PER_WEEK)\r\n if (weeks === 4) {\r\n return t('slug:number-span-ago', { number: 1, span: timeSpans['M'] })\r\n } else {\r\n return t('slug:number-span-ago', { number: weeks, span: timeSpans[weeks === 1 ? 'w' : 'ww'] })\r\n }\r\n } else {\r\n return t('slug:number-span-ago', { number, span: timeSpans[key] })\r\n }\r\n }\r\n\r\n moment.relativeTimeRounding(Math.floor)\r\n moment.relativeTimeThreshold('s', 60)\r\n moment.relativeTimeThreshold('m', 60)\r\n moment.relativeTimeThreshold('h', 24)\r\n moment.relativeTimeThreshold('d', 31)\r\n moment.relativeTimeThreshold('w', 4)\r\n moment.relativeTimeThreshold('M', 12)\r\n moment.updateLocale('en', {\r\n relativeTime: {\r\n s: _relativeTimeParser,\r\n ss: _relativeTimeParser,\r\n m: _relativeTimeParser,\r\n mm: _relativeTimeParser,\r\n h: _relativeTimeParser,\r\n hh: _relativeTimeParser,\r\n d: _relativeTimeParser,\r\n dd: _relativeTimeParser,\r\n w: _relativeTimeParser,\r\n ww: _relativeTimeParser,\r\n M: _relativeTimeParser,\r\n MM: _relativeTimeParser,\r\n y: _relativeTimeParser,\r\n yy: _relativeTimeParser\r\n }\r\n })\r\n }, [])\r\n\r\n // If category is not ALL, show an even number of posts\r\n const POSTS_TO_SHOW = state.category === '' ? INITIAL_POSTS_TO_SHOW : INITIAL_POSTS_TO_SHOW + 1\r\n\r\n const handleSearchTextChangeWithDebounce = debounce((value) => {\r\n dispatch({ type: 'set', payload: { query: value } })\r\n }, 300)\r\n\r\n const handleCategoryChange = (_, category) => {\r\n dispatch({ type: 'set', payload: { category } })\r\n }\r\n\r\n return (\r\n