import { parse as parseCSV } from 'papaparse' ; import * as React from 'react' ; import Redux from 'redux' ; import { SHEETS } from '../Constants' ; import { icon } from '../helpers' ; import { CardType , FiltersState , TranslationsType } from '../reducers/StateTypes' ; import { getStore } from '../Store' ; import { CardsFilterAction , CardsLoadingAction , CardsUpdateAction , TranslationsUpdateAction } from './ActionTypes' ; import { filtersCalculate } from './Filters' ; interface ResultType { cards ? : CardType [ ] ; translations ? : TranslationsType ; } export function downloadCards ( name , cardType = null ) { return ( dispatch ) => { const store = getStore ( ) ; dispatch ( cardsLoading ( ) ) ; const p = ( cardType !== null ) ? Promise . all ( [ parseSingleSheet ( cardType , name ) ] ) : downloadAndProcessSpreadsheet ( name ) ; p . then ( ( results ) => { const cards = results . reduce ( ( acc , obj ) => { return [ ... acc , ... ( obj . cards || [ ] ) ] ; } , [ ] ) . sort ( ( a , b ) => { if ( a . sheet < b . sheet ) { return - 1 ; } else if ( a . sheet > b . sheet ) { return 1 ; } else if ( a . class < b . class ) { return - 1 ; } else if ( a . class > b . class ) { return 1 ; } return 0 ; } ) ; const translations = results . reduce ( ( acc , obj ) => { return { ... acc , ... ( obj . translations || { } ) } ; } , { } ) ; if ( Object . keys ( translations ) . length > 0 ) { dispatch ( translationsUpdate ( { AdjectiveAfterNoun : false , ... translations } ) ) ; } dispatch ( cardsUpdate ( cards ) ) ; dispatch ( cardsFilter ( cards , store . getState ( ) . filters ) ) ; dispatch ( filtersCalculate ( store . getState ( ) . cards . filtered ) ) ; } ) ; } ; } function parseSingleSheet ( sheetName , url ) { return new Promise ( ( resolve , reject ) => { parseCSV ( url , { download : true , header : true , dynamicTyping : true , complete ( results ) { const data = results . data ; let cards = [ ] ; let translations = null ; if ( sheetName === 'translations' ) { translations = { AdjectiveAfterNoun : false } ; for ( const row of data ) { if ( row . Translated && row . Translated !== '' ) { translations [ row . Language . toLowerCase ( ) ] = row . Translated ; } } } else { cards = cards . concat ( data . filter ( ( card ) => { return ( card . Comment === null || card . hide === null ) ; } ) . map ( ( card ) => { card . sheet = sheetName ; return card ; } ) ) ; } resolve ( { cards , translations } ) ; } , } ) ; } ) ; } function downloadAndProcessSpreadsheet ( name ) { let source = null ; for ( const s of SHEETS ) { if ( name === s . name ) { source = s ; break ; } } const promises = [ ] ; for ( const sheetName of Object . keys ( source . sheets ) ) { const url = 'https://docs.google.com/spreadsheets/d/e/' + source . key + '/pub?output=csv&single=true&gid=' + source . sheets [ sheetName ] ; promises . push ( parseSingleSheet ( sheetName , url ) ) ; } return Promise . all ( promises ) ; } export function cardsLoading ( ) { return { type : 'CARDS_LOADING' } ; } export function cardsUpdate ( cards ) { return { type : 'CARDS_UPDATE' , cards } ; } export function translationsUpdate ( translations ) { return { type : 'TRANSLATIONS_UPDATE' , translations } ; } export function cardsFilter ( cards , filters ) { return { type : 'CARDS_FILTER' , cards , filters } ; } export function filterAndFormatCards ( cards , filters ) { if ( cards === null ) { return cards ; } const cardFilters = [ 'sheet' , 'tier' , 'class' ] . filter ( ( filterName ) => { return ( filters [ filterName ] . current !== 'All' ) ; } ) ; return cards . filter ( ( card ) => { for ( const filterName of cardFilters ) { const filter = filters [ filterName ] ; if ( card [ filterName ] !== filter . current ) { return false ; } } return true ; } ) . map ( ( card ) => formatCard ( card , filters ) ) ; } const iconRegex = /#\w*/mg ; const boldColonedRegex = /[^#:.\n]*?:/g ; const orRegex = /\nOR\n/g ; const symbolRegex = /&[#a-z0-9]{1,7};/img ; const doubleLinebreak = /\n\n/mg ; const singleLinebreak = /\n/mg ; const elementifyRegex = new RegExp ( '(' + [ iconRegex . source , boldColonedRegex . source , orRegex . source , symbolRegex . source , doubleLinebreak . source , singleLinebreak . source , ] . join ( '|' ) + ')' , 'igm' ) ; function formatCard ( card , filters ) { const newCard = { } ; Object . keys ( card ) . forEach ( ( key ) => { let value = card [ key ] ; if ( value === '-' || value === '' ) { return ; } else if ( typeof value === 'string' ) { const values = value . split ( elementifyRegex ) . filter ( ( str ) => ( str && str !== '' ) ) . map ( ( str , index ) : string | JSX . Element => { if ( iconRegex . test ( str ) ) { let theme = filters . theme . current ; if ( filters . theme . current === 'Color' || filters . theme . current === 'BlackAndWhite' ) { theme = null ; } return icon ( str . replace ( iconRegex , ( match ) => match . substring ( 1 ) + '_small' ) , theme , index ) ; } if ( orRegex . test ( str ) ) { return < div key = { index } className = "or" > OR < /div>; } if ( boldColonedRegex . test ( str ) && key !== 'flavortext' ) { return < strong key = { index } > { str } < /strong>; } if ( symbolRegex . test ( str ) ) { return < span key = { index } className = "symbol" > { str } < /span>; } if ( doubleLinebreak . test ( str ) ) { return < br key = { index } className = "padded" / > ; } if ( singleLinebreak . test ( str ) ) { return < br key = { index } />; } return str ; } ) ; if ( values . length === 0 ) { value = null ; } else if ( values . length === 1 ) { value = values [ 0 ] ; } else { value = values ; } } newCard [ key ] = value ; } ) ; return newCard ; }