Add state management
parent
92ca49ab9a
commit
d6942bffab
|
@ -10,7 +10,8 @@ module.exports = {
|
||||||
'@typescript-eslint/no-shadow': 'off',
|
'@typescript-eslint/no-shadow': 'off',
|
||||||
'no-shadow': 'off',
|
'no-shadow': 'off',
|
||||||
'no-undef': 'off',
|
'no-undef': 'off',
|
||||||
|
semi: [2, 'never'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
semi: false,
|
||||||
arrowParens: 'avoid',
|
arrowParens: 'avoid',
|
||||||
bracketSameLine: true,
|
bracketSameLine: true,
|
||||||
bracketSpacing: false,
|
bracketSpacing: false,
|
||||||
singleQuote: true,
|
singleQuote: true,
|
||||||
trailingComma: 'all',
|
trailingComma: 'all',
|
||||||
};
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ Uses:
|
||||||
- [React Native](https://reactnative.dev)
|
- [React Native](https://reactnative.dev)
|
||||||
- [React Native for Web](https://necolas.github.io/react-native-web/)
|
- [React Native for Web](https://necolas.github.io/react-native-web/)
|
||||||
- [React Navigation](https://reactnative.dev/docs/navigation#react-navigation)
|
- [React Navigation](https://reactnative.dev/docs/navigation#react-navigation)
|
||||||
- (todo) [MobX](https://mobx.js.org/README.html) and [MobX State Tree](https://mobx-state-tree.js.org/)
|
- [MobX](https://mobx.js.org/README.html) and [MobX State Tree](https://mobx-state-tree.js.org/)
|
||||||
- (todo) [Async Storage](https://github.com/react-native-async-storage/async-storage)
|
- [Async Storage](https://github.com/react-native-async-storage/async-storage)
|
||||||
|
|
||||||
## Build instructions
|
## Build instructions
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,15 @@
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'react-native';
|
import 'react-native'
|
||||||
import React from 'react';
|
import React from 'react'
|
||||||
import App from '../src/App';
|
import App from '../src/App'
|
||||||
|
|
||||||
// Note: test renderer must be required after react-native.
|
// Note: test renderer must be required after react-native.
|
||||||
import renderer from 'react-test-renderer';
|
import renderer from 'react-test-renderer'
|
||||||
|
|
||||||
it('renders correctly', () => {
|
it('renders correctly', () => {
|
||||||
renderer.act(() => {
|
renderer.act(() => {
|
||||||
renderer.create(<App />);
|
renderer.create(<App />)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
presets: ['module:metro-react-native-babel-preset'],
|
presets: ['module:metro-react-native-babel-preset'],
|
||||||
};
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AppRegistry} from 'react-native';
|
import {AppRegistry} from 'react-native'
|
||||||
import App from './src/App';
|
import App from './src/App'
|
||||||
import {name as appName} from './src/app.json';
|
import {name as appName} from './src/app.json'
|
||||||
|
|
||||||
AppRegistry.registerComponent(appName, () => App);
|
AppRegistry.registerComponent(appName, () => App)
|
||||||
|
|
|
@ -14,4 +14,4 @@ module.exports = {
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
|
@ -11,8 +11,11 @@
|
||||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx"
|
"lint": "eslint . --ext .js,.jsx,.ts,.tsx"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@react-native-async-storage/async-storage": "^1.17.6",
|
||||||
"@react-navigation/native": "^6.0.10",
|
"@react-navigation/native": "^6.0.10",
|
||||||
"@react-navigation/native-stack": "^6.6.2",
|
"@react-navigation/native-stack": "^6.6.2",
|
||||||
|
"mobx": "^6.6.0",
|
||||||
|
"mobx-state-tree": "^5.1.5",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
"react-native": "0.68.2",
|
"react-native": "0.68.2",
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
import React, {useState, useEffect} from 'react'
|
||||||
|
import {
|
||||||
|
SafeAreaView,
|
||||||
|
ScrollView,
|
||||||
|
StatusBar,
|
||||||
|
Text,
|
||||||
|
Button,
|
||||||
|
useColorScheme,
|
||||||
|
View,
|
||||||
|
} from 'react-native'
|
||||||
|
import {NavigationContainer} from '@react-navigation/native'
|
||||||
|
import {
|
||||||
|
createNativeStackNavigator,
|
||||||
|
NativeStackScreenProps,
|
||||||
|
} from '@react-navigation/native-stack'
|
||||||
|
import {RootStore, setupState, RootStoreProvider} from './state'
|
||||||
|
|
||||||
|
type RootStackParamList = {
|
||||||
|
Home: undefined
|
||||||
|
Profile: {name: string}
|
||||||
|
}
|
||||||
|
const Stack = createNativeStackNavigator()
|
||||||
|
|
||||||
|
const HomeScreen = ({
|
||||||
|
navigation,
|
||||||
|
}: NativeStackScreenProps<RootStackParamList, 'Home'>) => {
|
||||||
|
const isDarkMode = useColorScheme() === 'dark'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView>
|
||||||
|
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
|
||||||
|
<ScrollView contentInsetAdjustmentBehavior="automatic">
|
||||||
|
<View>
|
||||||
|
<Text>Native</Text>
|
||||||
|
<Button
|
||||||
|
title="Go to Jane's profile"
|
||||||
|
onPress={() => navigation.navigate('Profile', {name: 'Jane'})}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
</SafeAreaView>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProfileScreen = ({
|
||||||
|
route,
|
||||||
|
}: NativeStackScreenProps<RootStackParamList, 'Profile'>) => {
|
||||||
|
return <Text>This is {route.params.name}'s profile</Text>
|
||||||
|
}
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [rootStore, setRootStore] = useState<RootStore | undefined>(undefined)
|
||||||
|
|
||||||
|
// init
|
||||||
|
useEffect(() => {
|
||||||
|
setupState().then(setRootStore)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// show nothing prior to init
|
||||||
|
if (!rootStore) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RootStoreProvider value={rootStore}>
|
||||||
|
<NavigationContainer>
|
||||||
|
<Stack.Navigator>
|
||||||
|
<Stack.Screen
|
||||||
|
name="Home"
|
||||||
|
component={HomeScreen}
|
||||||
|
options={{title: 'Welcome'}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen name="Profile" component={ProfileScreen} />
|
||||||
|
</Stack.Navigator>
|
||||||
|
</NavigationContainer>
|
||||||
|
</RootStoreProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
112
src/App.tsx
112
src/App.tsx
|
@ -1,112 +0,0 @@
|
||||||
/**
|
|
||||||
* Sample React Native App
|
|
||||||
* https://github.com/facebook/react-native
|
|
||||||
*
|
|
||||||
* Generated with the TypeScript template
|
|
||||||
* https://github.com/react-native-community/react-native-template-typescript
|
|
||||||
*
|
|
||||||
* @format
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import {
|
|
||||||
SafeAreaView,
|
|
||||||
ScrollView,
|
|
||||||
StatusBar,
|
|
||||||
StyleSheet,
|
|
||||||
Text,
|
|
||||||
Button,
|
|
||||||
useColorScheme,
|
|
||||||
View,
|
|
||||||
} from 'react-native';
|
|
||||||
import {NavigationContainer} from '@react-navigation/native';
|
|
||||||
import {
|
|
||||||
createNativeStackNavigator,
|
|
||||||
NativeStackScreenProps,
|
|
||||||
} from '@react-navigation/native-stack';
|
|
||||||
|
|
||||||
type RootStackParamList = {
|
|
||||||
Home: undefined;
|
|
||||||
Profile: {name: string};
|
|
||||||
};
|
|
||||||
const Stack = createNativeStackNavigator();
|
|
||||||
|
|
||||||
const Section: React.FC<{
|
|
||||||
title: string;
|
|
||||||
}> = ({children, title}) => {
|
|
||||||
return (
|
|
||||||
<View style={styles.sectionContainer}>
|
|
||||||
<Text style={styles.sectionTitle}>{title}</Text>
|
|
||||||
<Text style={styles.sectionDescription}>{children}</Text>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const HomeScreen = ({
|
|
||||||
navigation,
|
|
||||||
}: NativeStackScreenProps<RootStackParamList, 'Home'>) => {
|
|
||||||
const isDarkMode = useColorScheme() === 'dark';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SafeAreaView>
|
|
||||||
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
|
|
||||||
<ScrollView contentInsetAdjustmentBehavior="automatic">
|
|
||||||
<View>
|
|
||||||
<Section title="Step One">
|
|
||||||
Edit <Text style={styles.highlight}>App.tsx</Text> to change this
|
|
||||||
screen and then come back to see your edits.
|
|
||||||
<Button
|
|
||||||
title="Go to Jane's profile"
|
|
||||||
onPress={() => navigation.navigate('Profile', {name: 'Jane'})}
|
|
||||||
/>
|
|
||||||
</Section>
|
|
||||||
<Section title="Learn More">
|
|
||||||
Read the docs to discover what to do next:
|
|
||||||
</Section>
|
|
||||||
</View>
|
|
||||||
</ScrollView>
|
|
||||||
</SafeAreaView>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const ProfileScreen = ({
|
|
||||||
route,
|
|
||||||
}: NativeStackScreenProps<RootStackParamList, 'Profile'>) => {
|
|
||||||
return <Text>This is {route.params.name}'s profile</Text>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const App = () => {
|
|
||||||
return (
|
|
||||||
<NavigationContainer>
|
|
||||||
<Stack.Navigator>
|
|
||||||
<Stack.Screen
|
|
||||||
name="Home"
|
|
||||||
component={HomeScreen}
|
|
||||||
options={{title: 'Welcome'}}
|
|
||||||
/>
|
|
||||||
<Stack.Screen name="Profile" component={ProfileScreen} />
|
|
||||||
</Stack.Navigator>
|
|
||||||
</NavigationContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
sectionContainer: {
|
|
||||||
marginTop: 32,
|
|
||||||
paddingHorizontal: 24,
|
|
||||||
},
|
|
||||||
sectionTitle: {
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: '600',
|
|
||||||
},
|
|
||||||
sectionDescription: {
|
|
||||||
marginTop: 8,
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: '400',
|
|
||||||
},
|
|
||||||
highlight: {
|
|
||||||
fontWeight: '700',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default App;
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
import React, {useState, useEffect} from 'react'
|
||||||
|
import {
|
||||||
|
SafeAreaView,
|
||||||
|
ScrollView,
|
||||||
|
StatusBar,
|
||||||
|
Text,
|
||||||
|
Button,
|
||||||
|
useColorScheme,
|
||||||
|
View,
|
||||||
|
} from 'react-native'
|
||||||
|
import {NavigationContainer} from '@react-navigation/native'
|
||||||
|
import {
|
||||||
|
createNativeStackNavigator,
|
||||||
|
NativeStackScreenProps,
|
||||||
|
} from '@react-navigation/native-stack'
|
||||||
|
import {RootStore, setupState, RootStoreProvider} from './state'
|
||||||
|
|
||||||
|
type RootStackParamList = {
|
||||||
|
Home: undefined
|
||||||
|
Profile: {name: string}
|
||||||
|
}
|
||||||
|
const Stack = createNativeStackNavigator()
|
||||||
|
|
||||||
|
const HomeScreen = ({
|
||||||
|
navigation,
|
||||||
|
}: NativeStackScreenProps<RootStackParamList, 'Home'>) => {
|
||||||
|
const isDarkMode = useColorScheme() === 'dark'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView>
|
||||||
|
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
|
||||||
|
<ScrollView contentInsetAdjustmentBehavior="automatic">
|
||||||
|
<View>
|
||||||
|
<Text>Web</Text>
|
||||||
|
<Button
|
||||||
|
title="Go to Jane's profile"
|
||||||
|
onPress={() => navigation.navigate('Profile', {name: 'Jane'})}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
</SafeAreaView>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProfileScreen = ({
|
||||||
|
route,
|
||||||
|
}: NativeStackScreenProps<RootStackParamList, 'Profile'>) => {
|
||||||
|
return <Text>This is {route.params.name}'s profile</Text>
|
||||||
|
}
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [rootStore, setRootStore] = useState<RootStore | undefined>(undefined)
|
||||||
|
|
||||||
|
// init
|
||||||
|
useEffect(() => {
|
||||||
|
setupState().then(setRootStore)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// show nothing prior to init
|
||||||
|
if (!rootStore) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RootStoreProvider value={rootStore}>
|
||||||
|
<NavigationContainer>
|
||||||
|
<Stack.Navigator>
|
||||||
|
<Stack.Screen
|
||||||
|
name="Home"
|
||||||
|
component={HomeScreen}
|
||||||
|
options={{title: 'Welcome'}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen name="Profile" component={ProfileScreen} />
|
||||||
|
</Stack.Navigator>
|
||||||
|
</NavigationContainer>
|
||||||
|
</RootStoreProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
|
@ -2,11 +2,11 @@
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AppRegistry} from 'react-native';
|
import {AppRegistry} from 'react-native'
|
||||||
import App from './App';
|
import App from './App'
|
||||||
|
|
||||||
AppRegistry.registerComponent('App', () => App);
|
AppRegistry.registerComponent('App', () => App)
|
||||||
|
|
||||||
AppRegistry.runApplication('App', {
|
AppRegistry.runApplication('App', {
|
||||||
rootTag: document.getElementById('root'),
|
rootTag: document.getElementById('root'),
|
||||||
});
|
})
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* The environment is a place where services and shared dependencies between
|
||||||
|
* models live. They are made available to every model via dependency injection.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {getEnv, IStateTreeNode} from 'mobx-state-tree'
|
||||||
|
|
||||||
|
export class Environment {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
async setup() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension to the MST models that adds the environment property.
|
||||||
|
* Usage:
|
||||||
|
*
|
||||||
|
* .extend(withEnvironment)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export const withEnvironment = (self: IStateTreeNode) => ({
|
||||||
|
views: {
|
||||||
|
get environment() {
|
||||||
|
return getEnv<Environment>(self)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
|
@ -0,0 +1,30 @@
|
||||||
|
import {onSnapshot} from 'mobx-state-tree'
|
||||||
|
import {RootStoreModel, RootStore} from './models/root-store'
|
||||||
|
import {Environment} from './env'
|
||||||
|
import * as storage from './storage'
|
||||||
|
|
||||||
|
const ROOT_STATE_STORAGE_KEY = 'root'
|
||||||
|
|
||||||
|
export async function setupState() {
|
||||||
|
let rootStore: RootStore
|
||||||
|
let data: any
|
||||||
|
|
||||||
|
const env = new Environment()
|
||||||
|
try {
|
||||||
|
data = (await storage.load(ROOT_STATE_STORAGE_KEY)) || {}
|
||||||
|
rootStore = RootStoreModel.create(data, env)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to load state from storage', e)
|
||||||
|
rootStore = RootStoreModel.create({}, env)
|
||||||
|
}
|
||||||
|
|
||||||
|
// track changes & save to storage
|
||||||
|
onSnapshot(rootStore, snapshot =>
|
||||||
|
storage.save(ROOT_STATE_STORAGE_KEY, snapshot),
|
||||||
|
)
|
||||||
|
|
||||||
|
return rootStore
|
||||||
|
}
|
||||||
|
|
||||||
|
export {useStores, RootStoreModel, RootStoreProvider} from './models/root-store'
|
||||||
|
export type {RootStore} from './models/root-store'
|
|
@ -0,0 +1,16 @@
|
||||||
|
/**
|
||||||
|
* The root store is the base of all modeled state.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Instance, SnapshotOut, types} from 'mobx-state-tree'
|
||||||
|
import {createContext, useContext} from 'react'
|
||||||
|
|
||||||
|
export const RootStoreModel = types.model('RootStore').props({})
|
||||||
|
|
||||||
|
export interface RootStore extends Instance<typeof RootStoreModel> {}
|
||||||
|
export interface RootStoreSnapshot extends SnapshotOut<typeof RootStoreModel> {}
|
||||||
|
|
||||||
|
// react context & hook utilities
|
||||||
|
const RootStoreContext = createContext<RootStore>({} as RootStore)
|
||||||
|
export const RootStoreProvider = RootStoreContext.Provider
|
||||||
|
export const useStores = () => useContext(RootStoreContext)
|
|
@ -0,0 +1,52 @@
|
||||||
|
import AsyncStorage from '@react-native-async-storage/async-storage'
|
||||||
|
|
||||||
|
export async function loadString(key: string): Promise<string | null> {
|
||||||
|
try {
|
||||||
|
return await AsyncStorage.getItem(key)
|
||||||
|
} catch {
|
||||||
|
// not sure why this would fail... even reading the RN docs I'm unclear
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveString(key: string, value: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await AsyncStorage.setItem(key, value)
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function load(key: string): Promise<any | null> {
|
||||||
|
try {
|
||||||
|
const str = await AsyncStorage.getItem(key)
|
||||||
|
if (typeof str !== 'string') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return JSON.parse(str)
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function save(key: string, value: any): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await AsyncStorage.setItem(key, JSON.stringify(value))
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function remove(key: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
await AsyncStorage.removeItem(key)
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function clear(): Promise<void> {
|
||||||
|
try {
|
||||||
|
await AsyncStorage.clear()
|
||||||
|
} catch {}
|
||||||
|
}
|
29
yarn.lock
29
yarn.lock
|
@ -1797,6 +1797,13 @@
|
||||||
schema-utils "^3.0.0"
|
schema-utils "^3.0.0"
|
||||||
source-map "^0.7.3"
|
source-map "^0.7.3"
|
||||||
|
|
||||||
|
"@react-native-async-storage/async-storage@^1.17.6":
|
||||||
|
version "1.17.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@react-native-async-storage/async-storage/-/async-storage-1.17.6.tgz#ddb3520d051f71698c8a0e79e8959a7bf6d9f43b"
|
||||||
|
integrity sha512-XXnoheQI3vQTQmjphdXNLTmtiKZeRqvI8kPQ25X5Eae7nZjdYEEGN+0z8N2qyelbUIQwKgmW0aagJk56q7DyNg==
|
||||||
|
dependencies:
|
||||||
|
merge-options "^3.0.4"
|
||||||
|
|
||||||
"@react-native-community/cli-debugger-ui@^7.0.3":
|
"@react-native-community/cli-debugger-ui@^7.0.3":
|
||||||
version "7.0.3"
|
version "7.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-7.0.3.tgz#3eeeacc5a43513cbcae56e5e965d77726361bcb4"
|
resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-7.0.3.tgz#3eeeacc5a43513cbcae56e5e965d77726361bcb4"
|
||||||
|
@ -6634,6 +6641,11 @@ is-obj@^1.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
|
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
|
||||||
integrity sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==
|
integrity sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==
|
||||||
|
|
||||||
|
is-plain-obj@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
|
||||||
|
integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
|
||||||
|
|
||||||
is-plain-obj@^3.0.0:
|
is-plain-obj@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7"
|
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7"
|
||||||
|
@ -8128,6 +8140,13 @@ merge-descriptors@1.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
||||||
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
|
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
|
||||||
|
|
||||||
|
merge-options@^3.0.4:
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/merge-options/-/merge-options-3.0.4.tgz#84709c2aa2a4b24c1981f66c179fe5565cc6dbb7"
|
||||||
|
integrity sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==
|
||||||
|
dependencies:
|
||||||
|
is-plain-obj "^2.1.0"
|
||||||
|
|
||||||
merge-stream@^2.0.0:
|
merge-stream@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||||
|
@ -8506,6 +8525,16 @@ mkdirp@^0.5.1, mkdirp@~0.5.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.6"
|
minimist "^1.2.6"
|
||||||
|
|
||||||
|
mobx-state-tree@^5.1.5:
|
||||||
|
version "5.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/mobx-state-tree/-/mobx-state-tree-5.1.5.tgz#7344d61072705747abb98d23ad21302e38200105"
|
||||||
|
integrity sha512-jugIic0PYWW+nzzYfp4RUy9dec002Z778OC6KzoOyBHnqxupK9iPCsUJYkHjmNRHjZ8E4Z7qQpsKV3At/ntGVw==
|
||||||
|
|
||||||
|
mobx@^6.6.0:
|
||||||
|
version "6.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.6.0.tgz#617ca1f3b745a781fa89c5eb94a773e3cbeff8ae"
|
||||||
|
integrity sha512-MNTKevLH/6DShLZcmSL351+JgiJPO56A4GUpoiDQ3/yZ0mAtclNLdHK9q4BcQhibx8/JSDupfTpbX2NZPemlRg==
|
||||||
|
|
||||||
ms@2.0.0:
|
ms@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
|
|
Loading…
Reference in New Issue