Skip to content

Commit

Permalink
Merge pull request #26 from hooligram/feature/setup-state-persistence
Browse files Browse the repository at this point in the history
feature/setup-state-persistence
  • Loading branch information
imranariffin authored Feb 9, 2019
2 parents 344a9af + 2338051 commit 13f4e03
Show file tree
Hide file tree
Showing 16 changed files with 588 additions and 10 deletions.
7 changes: 7 additions & 0 deletions app/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { Component } from 'react'
import { Provider } from 'react-redux'
import { init } from '@state/actions/app'
import { apiInit } from '@state/actions/api'
import store from '@state/store'
import OnboardingStackNavigation from '@navigation/onboarding-stack'
import { setTopLevelNavigator } from '@state/middlewares/navigation/navigation'
Expand All @@ -13,5 +15,10 @@ export default class App extends Component {
)
}

componentDidMount() {
store.dispatch(init())
store.dispatch(apiInit())
}

goToNextScreen = () => console.log('goToNextScreen')
}
127 changes: 127 additions & 0 deletions app/persistence-api/__tests__/persistence-api.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// import PersistenceApi from '@persistence-api/persistence-api'

describe('persistence-api', () => {
describe('getState', () => {
describe('state exists in storage', () => {
let PersistenceApi
beforeEach(() => {
jest.resetAllMocks()
jest.mock('react-native', () => ({
AsyncStorage: {
getItem: jest.fn(() =>
new Promise(resolve =>
resolve('{"mockedData": "mockedData"}')
)
),
setItem: jest.fn()
}
}))
PersistenceApi = require('@persistence-api/persistence-api').default
})

it('should return state from storage', async () => {
const state = await PersistenceApi.getState()

expect(state).toEqual({
mockedData: 'mockedData'
})
})
})

describe('state does not exist in storage', () => {
let PersistenceApi
beforeEach(() => {
jest.resetAllMocks()
jest.doMock('react-native', () => ({
AsyncStorage: {
getItem: jest.fn(() => {
return new Promise(resolve =>
resolve('{}')
)
}),
setItem: jest.fn()
}
}))
PersistenceApi = require('@persistence-api/persistence-api').default
})

it('should return `undefined`', async () => {
const state = await PersistenceApi.getState()

expect(state).toEqual(undefined)
})
})
})

describe('saveState', () => {
let PersistenceApi, AsyncStorage, state
beforeEach(() => {
jest.resetAllMocks()
jest.mock('react-native', () => ({
AsyncStorage: {
getItem: jest.fn(),
setItem: jest.fn((key, state) =>
new Promise(resolve =>
resolve(undefined)
)
)
}
}))
PersistenceApi = require('@persistence-api/persistence-api').default
AsyncStorage = require('react-native').AsyncStorage
})

describe('state is serializable', () => {
beforeEach(() => {
state = {
someState: 'some state'
}
})

it('should save state as string', async () => {
await PersistenceApi.saveState(state)

expect(AsyncStorage.setItem).toHaveBeenCalledWith('STORAGE:STATE', '{\"someState\":\"some state\"}')
})
})

describe('state is not serializable', () => {
let state
beforeEach(() => {
state = {};
state.myself = state;
})

it('should throw error', async done => {
try {
await PersistenceApi.saveState(state)
fail()
}
catch (err) {
done()
}
})
})

describe('Storage fails to save', () => {
let state
beforeEach(() => {
AsyncStorage.setItem = jest.fn(() =>
new Promise((_, reject) =>
reject()
)
)
})

it('should throw error', async done => {
try {
await PersistenceApi.saveState(state)
fail()
}
catch (err) {
done()
}
})
})
})
})
3 changes: 3 additions & 0 deletions app/persistence-api/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import persistenceApi from './persistence-api'

export default persistenceApi
3 changes: 3 additions & 0 deletions app/persistence-api/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "@persistence-api"
}
20 changes: 20 additions & 0 deletions app/persistence-api/persistence-api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { AsyncStorage } from 'react-native'

const STORAGE_KEY_STATE = 'STORAGE:STATE'

export default class PersistenceApi {
static getState = async () => {
const result = await AsyncStorage.getItem(STORAGE_KEY_STATE)
try {
return JSON.parse(result)
}
catch (_) {
return undefined
}
}

static saveState = async (state) => {
const stateString = JSON.stringify(state)
await AsyncStorage.setItem(STORAGE_KEY_STATE, stateString)
}
}
8 changes: 8 additions & 0 deletions app/state/actions/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const INIT = 'INIT'

export const init = () => {
return {
type: INIT,
payload: {}
}
}
57 changes: 57 additions & 0 deletions app/state/actions/persistence.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
export const PERSISTENCE_LOAD_STATE_REQUEST = 'PERSISTENCE:LOAD_STATE_REQUEST'
export const PERSISTENCE_LOAD_STATE_SUCCESS = 'PERSISTENCE:LOAD_STATE_SUCCESS'
export const PERSISTENCE_LOAD_STATE_FAILURE = 'PERSISTENCE:LOAD_STATE_FAILURE'

export const PERSISTENCE_SAVE_STATE_REQUEST = 'PERSISTENCE:SAVE_STATE_REQUEST'
export const PERSISTENCE_SAVE_STATE_SUCCESS = 'PERSISTENCE:SAVE_STATE_SUCCESS'
export const PERSISTENCE_SAVE_STATE_FAILURE = 'PERSISTENCE:SAVE_STATE_FAILURE'

export const loadStateRequest = () => {
return {
type: PERSISTENCE_LOAD_STATE_REQUEST,
payload: {}
}
}

export const loadStateSuccess = (state) => {
return {
type: PERSISTENCE_LOAD_STATE_SUCCESS,
payload: {
state
}
}
}

export const loadStateFailure = (error) => {
return {
type: PERSISTENCE_LOAD_STATE_FAILURE,
payload: {
error
}
}
}

export const saveStateRequest = (state) => {
return {
type: PERSISTENCE_SAVE_STATE_REQUEST,
payload: {
state
}
}
}

export const saveStateSuccess = () => {
return {
type: PERSISTENCE_SAVE_STATE_SUCCESS,
payload: {}
}
}

export const saveStateFailure = (error) => {
return {
type: PERSISTENCE_SAVE_STATE_FAILURE,
payload: {
error
}
}
}
7 changes: 7 additions & 0 deletions app/state/middlewares/logger/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default store => next => action => {
console.log('prevState', store.getState())
console.log('action', action)
const nextAction = next(action)
console.log('nextState', store.getState())
return nextAction
}
Loading

0 comments on commit 13f4e03

Please sign in to comment.