Commit 3531285f authored by Genar Trias Ortiz's avatar Genar Trias Ortiz 🎉

WIP: Starting to implenet repositories

parent 95a09b73
......@@ -20,4 +20,3 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
/collection
/flow-typed
{
"title": "genar-radio",
"description": null,
"url": "dat://2988a35a94bddc041686d8d54f1323e85363137afcc87e6aeeef62301f6474e5"
}
\ No newline at end of file
This diff is collapsed.
......@@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"autoprefixer": "7.1.6",
"axios": "^0.18.0",
"babel-core": "6.26.0",
"babel-eslint": "7.2.3",
"babel-jest": "20.0.3",
......@@ -15,7 +16,7 @@
"css-loader": "0.28.7",
"dotenv": "4.0.0",
"dotenv-expand": "4.2.0",
"eslint": "4.10.0",
"eslint": "^5.6.1",
"eslint-config-react-app": "^2.1.0",
"eslint-loader": "1.9.0",
"eslint-plugin-flowtype": "2.39.1",
......@@ -97,19 +98,27 @@
]
},
"eslintConfig": {
"extends": "react-app",
"extends": [
"react-app"
],
"env": {
"jest": true
}
},
"plugins": [
"flowtype",
"jest"
]
},
"devDependencies": {
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"eslint-plugin-jest": "^21.22.1",
"flow-bin": "^0.81.0",
"js-yaml": "^3.12.0",
"redux-logger": "^3.0.6",
"redux-mock-store": "^1.5.1",
"redux-promise": "^0.5.3",
"redux-saga-test-plan": "^3.7.0",
"redux-thunk": "^2.2.0"
}
}
......@@ -7,7 +7,7 @@ import PlaylistContainer from './containers/PlaylistContainer'
import PlayerContainer from './containers/PlayerContainer'
import ImporterContainer from './containers/ImporterContainer'
import CollectionContainer from './containers/CollectionContainer'
import SearchBar from './components/SearchBar/SearchBar'
import SearchContainer from './containers/SearchContainer'
import configureStore from './store/configureStore'
import {
......@@ -40,7 +40,7 @@ const CollectionPage = () => {
const SearchPage = () => {
return (
<React.Fragment>
<SearchBar />
<SearchContainer />
</React.Fragment>
)
}
......
......@@ -3,7 +3,7 @@
import { Dispatch } from 'redux'
import * as types from '../constants/ActionTypes'
import * as db from '../db/collection-store';
import * as db from '../db/collection-store'
export const getVisibleSongsIds = (collection: any) => {
return collection.rows.slice(0,10).map((row) => {
......
......@@ -4,13 +4,15 @@ import { Dispatch } from 'redux'
import * as types from '../constants/ActionTypes'
export const setCurrentPlaying = (song) => {
import Song from '../entities/Song'
export const setCurrentPlaying = (song: Song) => {
return function (dispatch: Dispatch) {
dispatch({type: 'SET_CURRENT_PLAYING', song})
}
}
export const addToPlaylist = (song) => {
export const addToPlaylist = (song: Song) => {
return function (dispatch: Dispatch) {
dispatch({type: types.ADD_TO_PLAYLIST, song})
}
......
......@@ -10,3 +10,5 @@ export const ADD_TO_PLAYLIST = 'ADD_TO_PLAYLIST'
export const SET_CURRENT_PLAYING = 'SET_CURRENT_PLAYING'
export const START_SEARCH = 'START_SEARCH'
export const SEARCH_FULLFILLED = 'SEARCH_FULLFILLED'
export const SEARCH_REJECTED = 'SEARCH_REJECTED'
import { connect } from 'react-redux'
import SearchBar from '../components/SearchBar/SearchBar'
export default connect()(SearchBar)
// @flow
export default class Song {
}
// @flow
export type SearchAction = {
type: string,
searchTerm: string
}
// @flow
import IRepository from './IRepository'
export default class DummyRepository implements IRepository {
search(searchTerm: string) {
return [
{title: 'Highway to hell'}
]
}
}
// @flow
import axios from 'axios'
import IRepository from './IRepository'
export default class ItunesApiRepository implements IRepository {
search(searchTerm: string): Promise<any> {
return axios.get()
}
}
// @flow
import ItunesApiService from './ItunesApiService'
import ItunesApiRepository from './ItunesApiRepository'
describe('ItunesApiService', () => {
it('should handle song search', () => {
const itunesApiService = new ItunesApiService()
expect(itunesApiService.search('Bad brains')).to.be.an('array')
const itunesRepo = new ItunesApiRepository()
expect(itunesRepo.search('Bad brains')).toBeInstanceOf(Array)
})
})
// @flow
import { START_SEARCH } from '../constants/ActionTypes'
import { call, put, takeLatest } from 'redux-saga/effects'
function* search(action) {
try {
const user = yield call(Api.fetchUser, action.payload.userId);
yield put({type: "USER_FETCH_SUCCEEDED", user: user});
} catch (e) {
yield put({type: "USER_FETCH_FAILED", message: e.message});
}
import IRepository from '../repositories/IRepository'
import DummyRepository from '../repositories/DummyRepository'
import {
START_SEARCH,
SEARCH_FULLFILLED,
SEARCH_REJECTED
} from '../constants/ActionTypes'
import SongService from '../services/SongService'
import type { SearchAction } from '../reducers/search/SearchAction'
export function* search(repository: IRepository, action: SearchAction): Generator<void, void, void> {
const songService = new SongService(repository)
try {
const searchResults = yield call(songService.search, action.searchTerm)
yield put({type: SEARCH_FULLFILLED, searchResults})
} catch (e) {
yield put({type: SEARCH_REJECTED, message: e.message})
}
}
function* searchSaga() {
yield takeLatest(START_SEARCH, search)
function* searchSaga(): Generator<void, void, void> {
const repository = new DummyRepository()
yield takeLatest(START_SEARCH, search, [repository])
}
export default searchSaga
// @flow
import { take } from 'redux-saga/effects'
import { expectSaga } from 'redux-saga-test-plan'
import DummyRepository from '../repositories/DummyRepository'
import { START_SEARCH, SEARCH_FULLFILLED } from '../constants/ActionTypes'
import { search } from './search'
describe('search saga', () => {
it('should handle search events', () => {
const repository = new DummyRepository()
expectSaga(search, {type: START_SEARCH, searchTerm: 'Metallica'}, repository)
.put({type: SEARCH_FULLFILLED, searchResults: [] })
.run()
})
})
// @flow
export interface IApiService {
export interface IService {
search(searchTerm: string): Array<any>
}
// @flow
import IApiService from './IApiService'
export default class ItunesApiService implements IApiService {
search = (searchTerm: string) => {
}
}
// @flow
import { IRepository } from '../repositories/IRepository'
import { IService } from './IService'
export default class SongService implements IService {
songRepository: IRepository
constructor(repository: IRepository) {
this.songRepository = repository
}
search(searchTerm: string) {
return this.songRepository.search(searchTerm)
}
}
// @flow
import DummyRepository from '../repositories/DummyRepository'
import SongService from './SongService'
describe('SongService', () => {
it('should handle search', () => {
const dummyRepository = new DummyRepository()
const songService = new SongService(dummyRepository)
expect(songService.search('AC\\DC')).toEqual([{title: 'Highway to hell'}])
})
})
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment