refactor(app): move to typescript
This commit is contained in:
parent
081497ec97
commit
203a0a302b
|
@ -1 +1 @@
|
|||
export default from '@react-native-async-storage/async-storage/jest/async-storage-mock'
|
||||
export { default } from '@react-native-async-storage/async-storage/jest/async-storage-mock'
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { NavigationProp } from '@react-navigation/native'
|
||||
import { Layout, Text } from '@ui-kitten/components'
|
||||
import React, { useState } from 'react'
|
||||
import {
|
||||
|
@ -10,6 +11,11 @@ import {
|
|||
View,
|
||||
} from 'react-native'
|
||||
import { Login } from './login.component'
|
||||
import { SigninStackParamList } from './navigation.component'
|
||||
|
||||
interface AuthProps {
|
||||
navigation: NavigationProp<SigninStackParamList, 'Login'>
|
||||
}
|
||||
|
||||
const funArguments = [
|
||||
'agila',
|
||||
|
@ -31,7 +37,7 @@ const funArguments = [
|
|||
'öppna',
|
||||
]
|
||||
|
||||
export const Auth = (props) => {
|
||||
export const Auth = ({ navigation }: AuthProps) => {
|
||||
const [argument] = useState(() => {
|
||||
const argNum = Math.floor(Math.random() * funArguments.length)
|
||||
return funArguments[argNum]
|
||||
|
@ -52,7 +58,7 @@ export const Auth = (props) => {
|
|||
<Text category="h6" style={styles.subtitle}>
|
||||
Det {argument} alternativet
|
||||
</Text>
|
||||
<Login {...props} />
|
||||
<Login navigation={navigation} />
|
||||
</Layout>
|
||||
</View>
|
||||
</SafeAreaView>
|
|
@ -1,9 +1,10 @@
|
|||
import { useCalendar } from '@skolplattformen/api-hooks'
|
||||
import { CalendarItem } from '@skolplattformen/embedded-api'
|
||||
import { Divider, List, ListItem, Text } from '@ui-kitten/components'
|
||||
import moment from 'moment'
|
||||
import 'moment/locale/sv'
|
||||
import React from 'react'
|
||||
import { Image, StyleSheet, View } from 'react-native'
|
||||
import { Image, ListRenderItemInfo, StyleSheet, View } from 'react-native'
|
||||
import { useChild } from './childContext.component'
|
||||
import { CalendarOutlineIcon } from './icon.component'
|
||||
import { SaveToCalendar } from './saveToCalendar.component'
|
||||
|
@ -14,16 +15,6 @@ export const Calendar = () => {
|
|||
const child = useChild()
|
||||
const { data } = useCalendar(child)
|
||||
|
||||
const renderItem = ({ item }) => (
|
||||
<ListItem
|
||||
disabled={true}
|
||||
title={`${item.title}`}
|
||||
description={`${moment(item.startDate).fromNow()}`}
|
||||
accessoryLeft={CalendarOutlineIcon}
|
||||
accessoryRight={() => <SaveToCalendar event={item} />}
|
||||
/>
|
||||
)
|
||||
|
||||
return !data?.length ? (
|
||||
<View style={styles.emptyState}>
|
||||
<Image
|
||||
|
@ -35,9 +26,19 @@ export const Calendar = () => {
|
|||
) : (
|
||||
<List
|
||||
contentContainerStyle={styles.contentContainer}
|
||||
data={data.sort((a, b) => b.startDate < a.startDate)}
|
||||
data={data.sort((a, b) =>
|
||||
a.startDate && b.startDate ? b.startDate.localeCompare(a.startDate) : 0
|
||||
)}
|
||||
ItemSeparatorComponent={Divider}
|
||||
renderItem={renderItem}
|
||||
renderItem={({ item }: ListRenderItemInfo<CalendarItem>) => (
|
||||
<ListItem
|
||||
disabled={true}
|
||||
title={`${item.title}`}
|
||||
description={`${moment(item.startDate).fromNow()}`}
|
||||
accessoryLeft={CalendarOutlineIcon}
|
||||
accessoryRight={() => <SaveToCalendar event={item} />}
|
||||
/>
|
||||
)}
|
||||
style={styles.container}
|
||||
/>
|
||||
)
|
|
@ -1,4 +1,9 @@
|
|||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
|
||||
import {
|
||||
BottomTabBarOptions,
|
||||
BottomTabBarProps,
|
||||
createBottomTabNavigator,
|
||||
} from '@react-navigation/bottom-tabs'
|
||||
import { NavigationProp, RouteProp } from '@react-navigation/core'
|
||||
import {
|
||||
BottomNavigation,
|
||||
BottomNavigationTab,
|
||||
|
@ -8,7 +13,7 @@ import {
|
|||
TopNavigationAction,
|
||||
} from '@ui-kitten/components'
|
||||
import React from 'react'
|
||||
import { StyleSheet } from 'react-native'
|
||||
import { StyleProp, StyleSheet, TextProps } from 'react-native'
|
||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||
import { studentName } from '../utils/peopleHelpers'
|
||||
import { Calendar } from './calendar.component'
|
||||
|
@ -21,9 +26,20 @@ import {
|
|||
NewsIcon,
|
||||
NotificationsIcon,
|
||||
} from './icon.component'
|
||||
import { RootStackParamList } from './navigation.component'
|
||||
import { NewsList } from './newsList.component'
|
||||
import { NotificationsList } from './notificationsList.component'
|
||||
|
||||
interface ChildProps {
|
||||
navigation: NavigationProp<RootStackParamList, 'Child'>
|
||||
route: RouteProp<RootStackParamList, 'Child'>
|
||||
}
|
||||
|
||||
interface TabTitleProps {
|
||||
children: string
|
||||
style?: StyleProp<TextProps>
|
||||
}
|
||||
|
||||
const { Navigator, Screen } = createBottomTabNavigator()
|
||||
|
||||
const NewsScreen = () => {
|
||||
|
@ -58,13 +74,16 @@ const ClassmatesScreen = () => {
|
|||
)
|
||||
}
|
||||
|
||||
const TabTitle = ({ style, children }) => (
|
||||
const TabTitle = ({ style, children }: TabTitleProps) => (
|
||||
<Text adjustsFontSizeToFit numberOfLines={1} style={style}>
|
||||
{children}
|
||||
</Text>
|
||||
)
|
||||
|
||||
const BottomTabBar = ({ navigation, state }) => (
|
||||
const BottomTabBar = ({
|
||||
navigation,
|
||||
state,
|
||||
}: BottomTabBarProps<BottomTabBarOptions>) => (
|
||||
<BottomNavigation
|
||||
selectedIndex={state.index}
|
||||
onSelect={(index) => navigation.navigate(state.routeNames[index])}
|
||||
|
@ -100,8 +119,8 @@ const TabNavigator = ({ initialRouteName = 'Nyheter' }) => (
|
|||
</Navigator>
|
||||
)
|
||||
|
||||
export const Child = ({ route, navigation }) => {
|
||||
const { child, color, initialRouteName } = route.params
|
||||
export const Child = ({ route, navigation }: ChildProps) => {
|
||||
const { child, initialRouteName } = route.params
|
||||
|
||||
const BackAction = () => (
|
||||
<TopNavigationAction icon={BackIcon} onPress={navigateBack} />
|
||||
|
@ -112,7 +131,7 @@ export const Child = ({ route, navigation }) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ ...styles.wrap, color }}>
|
||||
<SafeAreaView style={{ ...styles.wrap }}>
|
||||
<ChildProvider child={child}>
|
||||
<TopNavigation
|
||||
title={studentName(child.name)}
|
|
@ -1,9 +0,0 @@
|
|||
import React, { createContext, useContext } from 'react'
|
||||
|
||||
export const ChildContext = createContext({})
|
||||
|
||||
export const ChildProvider = ({ child, children }) => {
|
||||
return <ChildContext.Provider value={child}>{children}</ChildContext.Provider>
|
||||
}
|
||||
|
||||
export const useChild = () => useContext(ChildContext)
|
|
@ -0,0 +1,19 @@
|
|||
import { Child } from '@skolplattformen/embedded-api'
|
||||
import React, { createContext, useContext } from 'react'
|
||||
|
||||
interface ChildProviderProps {
|
||||
child: Child
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export const ChildContext = createContext<Child>({
|
||||
id: '',
|
||||
sdsId: '',
|
||||
name: '',
|
||||
})
|
||||
|
||||
export const ChildProvider = ({ child, children }: ChildProviderProps) => {
|
||||
return <ChildContext.Provider value={child}>{children}</ChildContext.Provider>
|
||||
}
|
||||
|
||||
export const useChild = () => useContext(ChildContext)
|
|
@ -1,3 +1,4 @@
|
|||
import { NavigationProp } from '@react-navigation/core'
|
||||
import {
|
||||
useCalendar,
|
||||
useClassmates,
|
||||
|
@ -5,11 +6,13 @@ import {
|
|||
useNotifications,
|
||||
useSchedule,
|
||||
} from '@skolplattformen/api-hooks'
|
||||
import { Child } from '@skolplattformen/embedded-api'
|
||||
import { Avatar, Button, Card, Text } from '@ui-kitten/components'
|
||||
import { RenderProp } from '@ui-kitten/components/devsupport'
|
||||
import { DateTime } from 'luxon'
|
||||
import moment from 'moment'
|
||||
import React from 'react'
|
||||
import { StyleSheet, View } from 'react-native'
|
||||
import { StyleSheet, View, ViewProps } from 'react-native'
|
||||
import { studentName } from '../utils/peopleHelpers'
|
||||
import {
|
||||
CalendarOutlineIcon,
|
||||
|
@ -17,8 +20,19 @@ import {
|
|||
NewsIcon,
|
||||
NotificationsIcon,
|
||||
} from './icon.component'
|
||||
import { RootStackParamList } from './navigation.component'
|
||||
|
||||
export const ChildListItem = ({ navigation, child, color }) => {
|
||||
interface ChildListItemProps {
|
||||
child: Child
|
||||
color: string
|
||||
navigation: NavigationProp<RootStackParamList, 'Children'>
|
||||
}
|
||||
|
||||
export const ChildListItem = ({
|
||||
navigation,
|
||||
child,
|
||||
color,
|
||||
}: ChildListItemProps) => {
|
||||
// Forces rerender when child.id changes
|
||||
React.useEffect(() => {}, [child.id])
|
||||
|
||||
|
@ -30,12 +44,12 @@ export const ChildListItem = ({ navigation, child, color }) => {
|
|||
const { data: calendar, status: calendarStatus } = useCalendar(child)
|
||||
const { data: schedule } = useSchedule(
|
||||
child,
|
||||
DateTime.local(),
|
||||
DateTime.local().plus({ days: 7 })
|
||||
DateTime.local().toISO(),
|
||||
DateTime.local().plus({ days: 7 }).toISO()
|
||||
)
|
||||
|
||||
const notificationsThisWeek = notifications.filter((n) =>
|
||||
moment(n).isSame('week')
|
||||
moment(n.dateCreated).isSame('week')
|
||||
)
|
||||
|
||||
const scheduleAndCalendarThisWeek = [
|
||||
|
@ -55,23 +69,20 @@ export const ChildListItem = ({ navigation, child, color }) => {
|
|||
GR: 'Grundskolan',
|
||||
F: 'Förskoleklass',
|
||||
}
|
||||
|
||||
return child.status
|
||||
.split(';')
|
||||
.map((status) => abbrevations[status] || status)
|
||||
.join(', ')
|
||||
? child.status
|
||||
.split(';')
|
||||
.map((status) => {
|
||||
const statusAsAbbreviation = status as keyof typeof abbrevations
|
||||
|
||||
return abbrevations[statusAsAbbreviation] || status
|
||||
})
|
||||
.join(', ')
|
||||
: null
|
||||
}
|
||||
|
||||
const Header = (props) => (
|
||||
<View {...props} style={styles.cardHeader}>
|
||||
<View style={styles.cardAvatar}>
|
||||
<Avatar source={require('../assets/avatar.png')} shape="square" />
|
||||
</View>
|
||||
<View style={styles.cardHeaderText}>
|
||||
<Text category="h6">{studentName(child.name)}</Text>
|
||||
<Text category="s1">{`${getClassName()}`}</Text>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
const className = getClassName()
|
||||
|
||||
const Footer = () => (
|
||||
<View style={styles.itemFooter}>
|
||||
|
@ -143,17 +154,22 @@ export const ChildListItem = ({ navigation, child, color }) => {
|
|||
style={styles.card}
|
||||
appearance="filled"
|
||||
status={color}
|
||||
header={Header}
|
||||
header={(props) => (
|
||||
<View {...props} style={styles.cardHeader}>
|
||||
<View style={styles.cardAvatar}>
|
||||
<Avatar source={require('../assets/avatar.png')} shape="square" />
|
||||
</View>
|
||||
<View style={styles.cardHeaderText}>
|
||||
<Text category="h6">{studentName(child.name)}</Text>
|
||||
{className ? <Text category="s1">{className}</Text> : null}
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
footer={Footer}
|
||||
onPress={() => navigation.navigate('Child', { child, color })}
|
||||
>
|
||||
{scheduleAndCalendarThisWeek.slice(0, 3).map((calendarItem, i) => (
|
||||
<Text
|
||||
appearance="hint"
|
||||
category="c1"
|
||||
key={i}
|
||||
style={{ textColor: styles.loaded(notificationsStatus) }}
|
||||
>
|
||||
<Text appearance="hint" category="c1" key={i} style={styles.loaded}>
|
||||
{`${calendarItem.title}`}
|
||||
</Text>
|
||||
))}
|
||||
|
@ -213,4 +229,5 @@ const styles = StyleSheet.create({
|
|||
error: {
|
||||
color: '#500',
|
||||
},
|
||||
pending: {},
|
||||
})
|
|
@ -0,0 +1,163 @@
|
|||
import { NavigationProp } from '@react-navigation/core'
|
||||
import { useApi, useChildList } from '@skolplattformen/api-hooks'
|
||||
import { Child } from '@skolplattformen/embedded-api'
|
||||
import {
|
||||
Divider,
|
||||
Layout,
|
||||
List,
|
||||
Spinner,
|
||||
Text,
|
||||
TopNavigation,
|
||||
TopNavigationAction,
|
||||
} from '@ui-kitten/components'
|
||||
import React from 'react'
|
||||
import {
|
||||
Dimensions,
|
||||
Image,
|
||||
ListRenderItemInfo,
|
||||
SafeAreaView,
|
||||
StyleSheet,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import ActionSheet from 'rn-actionsheet-module'
|
||||
import { ChildListItem } from './childListItem.component'
|
||||
import { SettingsIcon } from './icon.component'
|
||||
import { RootStackParamList } from './navigation.component'
|
||||
|
||||
interface ChildrenProps {
|
||||
navigation: NavigationProp<RootStackParamList, 'Children'>
|
||||
}
|
||||
|
||||
const { width } = Dimensions.get('window')
|
||||
|
||||
const colors = ['primary', 'success', 'info', 'warning', 'danger']
|
||||
const settingsOptions = ['Logga ut', 'Avbryt']
|
||||
|
||||
export const Children = ({ navigation }: ChildrenProps) => {
|
||||
const { api } = useApi()
|
||||
const { data: childList, status } = useChildList()
|
||||
|
||||
const handleSettingSelection = (index: number) => {
|
||||
switch (index) {
|
||||
case 0:
|
||||
api.logout()
|
||||
navigation.navigate('Login')
|
||||
}
|
||||
}
|
||||
|
||||
const settings = () => {
|
||||
const options = {
|
||||
cancelButtonIndex: 1,
|
||||
title: 'Inställningar',
|
||||
optionsIOS: settingsOptions,
|
||||
optionsAndroid: settingsOptions,
|
||||
onCancelAndroidIndex: handleSettingSelection,
|
||||
}
|
||||
|
||||
ActionSheet(options, handleSettingSelection)
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.topContainer}>
|
||||
{status === 'loaded' ? (
|
||||
<>
|
||||
<TopNavigation
|
||||
title="Dina barn"
|
||||
alignment="center"
|
||||
accessoryRight={() => (
|
||||
<TopNavigationAction icon={SettingsIcon} onPress={settings} />
|
||||
)}
|
||||
/>
|
||||
<Divider />
|
||||
<List
|
||||
contentContainerStyle={styles.childListContainer}
|
||||
data={childList}
|
||||
style={styles.childList}
|
||||
ListEmptyComponent={
|
||||
<View style={styles.emptyState}>
|
||||
<Text category="h2">Inga barn</Text>
|
||||
<Text style={styles.emptyStateDescription}>
|
||||
Det finns inga barn registrerade för ditt personnummer i
|
||||
Stockholms Stad
|
||||
</Text>
|
||||
<Image
|
||||
source={require('../assets/children.png')}
|
||||
style={styles.emptyStateImage}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
renderItem={({ item: child, index }: ListRenderItemInfo<Child>) => (
|
||||
<ChildListItem
|
||||
child={child}
|
||||
color={colors[index % colors.length]}
|
||||
key={child.id}
|
||||
navigation={navigation}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Layout style={styles.loading}>
|
||||
<Image
|
||||
source={require('../assets/girls.png')}
|
||||
style={styles.loadingImage}
|
||||
/>
|
||||
<View style={styles.loadingMessage}>
|
||||
<Spinner size="large" status="warning" />
|
||||
<Text category="h1" style={styles.loadingText}>
|
||||
Laddar...
|
||||
</Text>
|
||||
</View>
|
||||
</Layout>
|
||||
)}
|
||||
</SafeAreaView>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
topContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
loading: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
loadingImage: {
|
||||
height: (width / 16) * 9,
|
||||
width: width,
|
||||
},
|
||||
loadingMessage: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
marginTop: 8,
|
||||
},
|
||||
loadingText: {
|
||||
marginLeft: 20,
|
||||
},
|
||||
childList: {
|
||||
flex: 1,
|
||||
},
|
||||
childListContainer: {
|
||||
padding: 20,
|
||||
},
|
||||
emptyState: {
|
||||
backgroundColor: '#fff',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 20,
|
||||
},
|
||||
emptyStateDescription: {
|
||||
lineHeight: 21,
|
||||
marginTop: 8,
|
||||
textAlign: 'center',
|
||||
},
|
||||
emptyStateImage: {
|
||||
// 80% size and 16:9 aspect ratio
|
||||
height: ((width * 0.8) / 16) * 9,
|
||||
marginTop: 20,
|
||||
width: width * 0.8,
|
||||
},
|
||||
})
|
|
@ -1,90 +0,0 @@
|
|||
// import { useClassmates } from '@skolplattformen/api-hooks'
|
||||
import { Card, Text } from '@ui-kitten/components'
|
||||
import React from 'react'
|
||||
import { View, StyleSheet } from 'react-native'
|
||||
// import { fullName, guardians, sortByFirstName } from '../utils/peopleHelpers'
|
||||
// import { useChild } from './childContext.component'
|
||||
// import { ContactMenu } from './contactMenu.component'
|
||||
|
||||
export const Classmates = () => {
|
||||
// const child = useChild()
|
||||
|
||||
/*
|
||||
const { data } = useClassmates(child)
|
||||
|
||||
const renderItemIcon = (props) => <Icon {...props} name="people-outline" />
|
||||
const [selected, setSelected] = React.useState()
|
||||
|
||||
const renderItem = ({ item, index }) => (
|
||||
<ListItem
|
||||
accessibilityLabel={`Barn ${index + 1}`}
|
||||
title={fullName(item)}
|
||||
onPress={() => setSelected(item)}
|
||||
description={guardians(item.guardians)}
|
||||
accessoryLeft={renderItemIcon}
|
||||
accessoryRight={() => (
|
||||
<ContactMenu
|
||||
contact={item}
|
||||
selected={item === selected}
|
||||
setSelected={setSelected}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
|
||||
return (
|
||||
<List
|
||||
style={styles.container}
|
||||
data={sortByFirstName(data)}
|
||||
ItemSeparatorComponent={Divider}
|
||||
ListHeaderComponent={
|
||||
<Text category="h5" style={styles.listHeader}>
|
||||
{data?.length ? `Klass ${data[0].className}` : 'Klass'}
|
||||
</Text>
|
||||
}
|
||||
renderItem={renderItem}
|
||||
contentContainerStyle={styles.contentContainer}
|
||||
/>
|
||||
)
|
||||
*/
|
||||
|
||||
const cardHeader = (props) => {
|
||||
return (
|
||||
<View style={styles.topContainer}>
|
||||
<Text category="h6">Klasslistan ej tillgänglig</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Card header={cardHeader} style={styles.contentContainer}>
|
||||
<Text>
|
||||
Klasslista kan tyvärr inte visas längre. Vi jobbar på att lösa det,
|
||||
och återkommer med information när vi vet mer.
|
||||
</Text>
|
||||
</Card>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
},
|
||||
contentContainer: {
|
||||
margin: 10,
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
topContainer: {
|
||||
margin: 5,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
listHeader: {
|
||||
backgroundColor: '#fff',
|
||||
paddingTop: 10,
|
||||
paddingLeft: 15,
|
||||
},
|
||||
})
|
|
@ -0,0 +1,45 @@
|
|||
import { Card, Text } from '@ui-kitten/components'
|
||||
import React from 'react'
|
||||
import { View, StyleSheet } from 'react-native'
|
||||
|
||||
export const Classmates = () => {
|
||||
const cardHeader = () => {
|
||||
return (
|
||||
<View style={styles.topContainer}>
|
||||
<Text category="h6">Klasslistan ej tillgänglig</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Card header={cardHeader} style={styles.contentContainer}>
|
||||
<Text>
|
||||
Klasslista kan tyvärr inte visas längre. Vi jobbar på att lösa det,
|
||||
och återkommer med information när vi vet mer.
|
||||
</Text>
|
||||
</Card>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
},
|
||||
contentContainer: {
|
||||
margin: 10,
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
topContainer: {
|
||||
margin: 5,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
listHeader: {
|
||||
backgroundColor: '#fff',
|
||||
paddingTop: 10,
|
||||
paddingLeft: 15,
|
||||
},
|
||||
})
|
|
@ -1,7 +1,7 @@
|
|||
import { Icon } from '@ui-kitten/components'
|
||||
import React from 'react'
|
||||
|
||||
const uiIcon = (name) => (props) => <Icon {...props} name={name} />
|
||||
const uiIcon = (name: string) => (props: any) => <Icon {...props} name={name} />
|
||||
|
||||
export const BackIcon = uiIcon('arrow-back')
|
||||
export const CalendarOutlineIcon = uiIcon('calendar-outline')
|
|
@ -1,33 +0,0 @@
|
|||
import { useApi } from '@skolplattformen/api-hooks'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Image as ImageBase } from 'react-native'
|
||||
|
||||
export const Image = ({ src, style }) => {
|
||||
const { api } = useApi()
|
||||
const [headers, setHeaders] = useState()
|
||||
|
||||
const getHeaders = async (url) => {
|
||||
// eslint-disable-next-line no-shadow
|
||||
const { headers } = await api.getSession(url)
|
||||
setHeaders(headers)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getHeaders(src)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [src])
|
||||
|
||||
return (
|
||||
<>
|
||||
{headers && (
|
||||
<ImageBase
|
||||
source={{
|
||||
uri: src,
|
||||
headers,
|
||||
}}
|
||||
style={style}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import { useApi } from '@skolplattformen/api-hooks'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Image as ImageBase, ImageStyle, StyleProp } from 'react-native'
|
||||
|
||||
interface ImageProps {
|
||||
src: string
|
||||
style: StyleProp<ImageStyle>
|
||||
}
|
||||
|
||||
export const Image = ({ src, style }: ImageProps) => {
|
||||
const { api } = useApi()
|
||||
const [headers, setHeaders] = useState()
|
||||
|
||||
const getHeaders = async (url: string) => {
|
||||
const { headers: newHeaders } = await api.getSession(url)
|
||||
setHeaders(newHeaders)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getHeaders(src)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [src])
|
||||
|
||||
return headers ? (
|
||||
<ImageBase
|
||||
source={{
|
||||
uri: src,
|
||||
headers,
|
||||
}}
|
||||
style={style}
|
||||
/>
|
||||
) : null
|
||||
}
|
|
@ -1,10 +1,15 @@
|
|||
import { Text } from '@ui-kitten/components'
|
||||
import React from 'react'
|
||||
import { Linking, StyleSheet } from 'react-native'
|
||||
import MarkdownBase from 'react-native-markdown-display'
|
||||
import MarkdownBase, { RenderRules } from 'react-native-markdown-display'
|
||||
import { Image } from './image.component'
|
||||
|
||||
const rules = {
|
||||
interface MarkdownProps {
|
||||
children: React.ReactNode
|
||||
style?: StyleSheet.NamedStyles<any>
|
||||
}
|
||||
|
||||
const rules: RenderRules = {
|
||||
image: (node) => {
|
||||
const { src } = node.attributes
|
||||
const url = src.startsWith('/')
|
||||
|
@ -13,19 +18,23 @@ const rules = {
|
|||
return <Image key={src} src={url} style={styles.markdownImage} />
|
||||
},
|
||||
link: (node, children, _parent, styles) => {
|
||||
return (
|
||||
<Text
|
||||
key={node.key}
|
||||
style={styles.link}
|
||||
onPress={() => Linking.openURL(node.attributes.href)}
|
||||
>
|
||||
{children}
|
||||
</Text>
|
||||
)
|
||||
if (typeof children === 'string') {
|
||||
return (
|
||||
<Text
|
||||
key={node.key}
|
||||
style={styles.link}
|
||||
onPress={() => Linking.openURL(node.attributes.href)}
|
||||
>
|
||||
{children}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
}
|
||||
|
||||
export const Markdown = ({ style, children }) => {
|
||||
export const Markdown = ({ style, children }: MarkdownProps) => {
|
||||
return (
|
||||
<MarkdownBase rules={rules} style={style}>
|
||||
{children}
|
|
@ -7,7 +7,12 @@ import { SafeAreaView } from 'react-native-safe-area-context'
|
|||
import { WebView } from 'react-native-webview'
|
||||
import { CloseIcon } from './icon.component'
|
||||
|
||||
export const ModalWebView = ({ url, onClose }) => {
|
||||
interface ModalWebViewProps {
|
||||
url: string
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
export const ModalWebView = ({ url, onClose }: ModalWebViewProps) => {
|
||||
const [modalVisible, setModalVisible] = React.useState(true)
|
||||
const { api } = useApi()
|
||||
const [headers, setHeaders] = useState()
|
|
@ -0,0 +1,59 @@
|
|||
import { NavigationContainer } from '@react-navigation/native'
|
||||
import { createStackNavigator } from '@react-navigation/stack'
|
||||
import React from 'react'
|
||||
import { StatusBar } from 'react-native'
|
||||
import { schema } from '../app.json'
|
||||
import Absence from './absence.component'
|
||||
import { Child } from './child.component'
|
||||
import { Children } from './children.component'
|
||||
import { Auth } from './auth.component'
|
||||
import { NewsItem } from './newsItem.component'
|
||||
import {
|
||||
Child as ChildType,
|
||||
NewsItem as NewsItemType,
|
||||
} from '@skolplattformen/embedded-api'
|
||||
|
||||
export type RootStackParamList = {
|
||||
Login: undefined
|
||||
Children: undefined
|
||||
Child: {
|
||||
child: ChildType
|
||||
color: string
|
||||
initialRouteName?: string
|
||||
}
|
||||
NewsItem: { newsItem: NewsItemType; child: ChildType }
|
||||
Absence: { child: ChildType }
|
||||
}
|
||||
|
||||
export type SigninStackParamList = {
|
||||
Login: undefined
|
||||
}
|
||||
|
||||
const { Navigator, Screen } = createStackNavigator()
|
||||
|
||||
const HomeNavigator = () => (
|
||||
<Navigator headerMode="none">
|
||||
<Screen name="Login" component={Auth} />
|
||||
<Screen name="Children" component={Children} />
|
||||
<Screen name="Child" component={Child} />
|
||||
<Screen name="NewsItem" component={NewsItem} />
|
||||
<Screen name="Absence" component={Absence} />
|
||||
</Navigator>
|
||||
)
|
||||
|
||||
const linking = {
|
||||
prefixes: [schema],
|
||||
config: {
|
||||
screens: {
|
||||
Login: 'login',
|
||||
},
|
||||
},
|
||||
}
|
||||
export const AppNavigator = () => {
|
||||
return (
|
||||
<NavigationContainer linking={linking}>
|
||||
<StatusBar />
|
||||
<HomeNavigator />
|
||||
</NavigationContainer>
|
||||
)
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
import { RouteProp } from '@react-navigation/native'
|
||||
import { StackNavigationProp } from '@react-navigation/stack'
|
||||
import { useNewsDetails } from '@skolplattformen/api-hooks'
|
||||
import {
|
||||
Divider,
|
||||
|
@ -12,13 +14,20 @@ import { SafeAreaView, ScrollView, StyleSheet, View } from 'react-native'
|
|||
import { BackIcon } from './icon.component'
|
||||
import { Image } from './image.component'
|
||||
import { Markdown } from './markdown.component'
|
||||
import { RootStackParamList } from './navigation.component'
|
||||
|
||||
const displayDate = (date) =>
|
||||
interface NewsItemProps {
|
||||
navigation: StackNavigationProp<RootStackParamList, 'NewsItem'>
|
||||
route: RouteProp<RootStackParamList, 'NewsItem'>
|
||||
}
|
||||
|
||||
const displayDate = (date: string | undefined) =>
|
||||
moment(date).locale('sv').format('DD MMM. YYYY HH:mm')
|
||||
|
||||
const dateIsValid = (date) => moment(date, moment.ISO_8601).isValid()
|
||||
const dateIsValid = (date: string | undefined) =>
|
||||
moment(date, moment.ISO_8601).isValid()
|
||||
|
||||
export const NewsItem = ({ navigation, route }) => {
|
||||
export const NewsItem = ({ navigation, route }: NewsItemProps) => {
|
||||
const { newsItem, child } = route.params
|
||||
const { data } = useNewsDetails(child, newsItem)
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { useNavigation } from '@react-navigation/native'
|
||||
import { NewsItem } from '@skolplattformen/embedded-api'
|
||||
import { DateTime } from 'luxon'
|
||||
import React from 'react'
|
||||
import { Dimensions, StyleSheet, Text, View } from 'react-native'
|
||||
|
@ -6,22 +7,29 @@ import { TouchableOpacity } from 'react-native-gesture-handler'
|
|||
import { useChild } from './childContext.component'
|
||||
import { Image } from './image.component'
|
||||
|
||||
interface NewsListItemProps {
|
||||
item: NewsItem
|
||||
}
|
||||
|
||||
const { width } = Dimensions.get('window')
|
||||
|
||||
export const NewsListItem = ({ item }) => {
|
||||
export const NewsListItem = ({ item }: NewsListItemProps) => {
|
||||
const navigation = useNavigation()
|
||||
const child = useChild()
|
||||
const hasDate = item.published || item.modified
|
||||
|
||||
const displayDate = DateTime.fromISO(
|
||||
item.published || item.modified
|
||||
).toRelative({ locale: 'sv', style: 'long' })
|
||||
const displayDate = hasDate
|
||||
? DateTime.fromISO(hasDate).toRelative({ locale: 'sv', style: 'long' })
|
||||
: null
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => navigation.navigate('NewsItem', { newsItem: item, child })}
|
||||
>
|
||||
<View style={styles.card}>
|
||||
{width > 320 && <Image src={item.fullImageUrl} style={styles.image} />}
|
||||
{width > 320 && item.fullImageUrl ? (
|
||||
<Image src={item.fullImageUrl} style={styles.image} />
|
||||
) : null}
|
||||
<View style={styles.text}>
|
||||
<View>
|
||||
<Text style={styles.title}>{item.header}</Text>
|
||||
|
@ -30,12 +38,7 @@ export const NewsListItem = ({ item }) => {
|
|||
{item.author && displayDate ? ' • ' : ''}
|
||||
{displayDate}
|
||||
</Text>
|
||||
<Text
|
||||
ellipsizeMode="tail"
|
||||
numberOfLines={2}
|
||||
category="s2"
|
||||
style={styles.intro}
|
||||
>
|
||||
<Text ellipsizeMode="tail" numberOfLines={2} style={styles.intro}>
|
||||
{item.intro}
|
||||
</Text>
|
||||
</View>
|
|
@ -1,10 +1,15 @@
|
|||
import { Notification as NotificationType } from '@skolplattformen/embedded-api'
|
||||
import { Card, Text } from '@ui-kitten/components'
|
||||
import { DateTime } from 'luxon'
|
||||
import React from 'react'
|
||||
import { StyleSheet, View } from 'react-native'
|
||||
import { ModalWebView } from './modalWebView.component'
|
||||
|
||||
export const Notification = ({ item }) => {
|
||||
interface NotificationProps {
|
||||
item: NotificationType
|
||||
}
|
||||
|
||||
export const Notification = ({ item }: NotificationProps) => {
|
||||
const [isOpen, setIsOpen] = React.useState(false)
|
||||
const open = () => setIsOpen(true)
|
||||
const close = () => setIsOpen(false)
|
|
@ -1,11 +1,18 @@
|
|||
import { Button, MenuItem, OverflowMenu } from '@ui-kitten/components'
|
||||
import React from 'react'
|
||||
import { StyleSheet } from 'react-native'
|
||||
import RNCalendarEvents from 'react-native-calendar-events'
|
||||
import RNCalendarEvents, {
|
||||
CalendarEventWritable,
|
||||
} from 'react-native-calendar-events'
|
||||
import { CalendarOutlineIcon, MoreIcon } from './icon.component'
|
||||
import Toast from 'react-native-simple-toast'
|
||||
import { CalendarItem } from '@skolplattformen/embedded-api'
|
||||
|
||||
export const SaveToCalendar = ({ event }) => {
|
||||
interface SaveToCalendarProps {
|
||||
event: CalendarItem
|
||||
}
|
||||
|
||||
export const SaveToCalendar = ({ event }: SaveToCalendarProps) => {
|
||||
const [visible, setVisible] = React.useState(false)
|
||||
|
||||
const renderToggleButton = () => (
|
||||
|
@ -21,7 +28,14 @@ export const SaveToCalendar = ({ event }) => {
|
|||
setVisible(false)
|
||||
}
|
||||
|
||||
const toast = (text) => Toast.showWithGravity(text, Toast.SHORT, Toast.BOTTOM)
|
||||
const toast = (text: string) =>
|
||||
Toast.showWithGravity(text, Toast.SHORT, Toast.BOTTOM)
|
||||
|
||||
function removeEmptyValues<T extends object>(obj: T) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj).filter(([_, v]) => v != null)
|
||||
) as { [K in keyof T]: any }
|
||||
}
|
||||
|
||||
const requestPermissionsAndSave = async ({
|
||||
title,
|
||||
|
@ -29,21 +43,23 @@ export const SaveToCalendar = ({ event }) => {
|
|||
endDate,
|
||||
location,
|
||||
description: notes,
|
||||
}) => {
|
||||
}: CalendarItem) => {
|
||||
const auth = await RNCalendarEvents.requestPermissions()
|
||||
|
||||
if (auth === 'authorized') {
|
||||
try {
|
||||
const details = {
|
||||
startDate: new Date(startDate).toISOString(),
|
||||
endDate: new Date(endDate).toISOString(),
|
||||
startDate: startDate
|
||||
? new Date(startDate).toISOString()
|
||||
: new Date().toISOString(),
|
||||
endDate: endDate
|
||||
? new Date(endDate).toISOString()
|
||||
: new Date().toISOString(),
|
||||
location,
|
||||
notes,
|
||||
}
|
||||
|
||||
const detailsWithoutEmpty = Object.fromEntries(
|
||||
Object.entries(details).filter(([_, v]) => v != null)
|
||||
)
|
||||
const detailsWithoutEmpty = removeEmptyValues(details)
|
||||
|
||||
await RNCalendarEvents.saveEvent(title, detailsWithoutEmpty)
|
||||
|
|
@ -4,12 +4,13 @@
|
|||
"scripts": {
|
||||
"android": "react-native run-android",
|
||||
"ios": "react-native-fix-image && react-native run-ios",
|
||||
"lint": "eslint .",
|
||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
|
||||
"pod": "npx pod-install",
|
||||
"start": "react-native start",
|
||||
"test": "is-ci-cli test:ci test:watch",
|
||||
"test:ci": "jest",
|
||||
"test:watch": "jest --watch"
|
||||
"test:watch": "jest --watch",
|
||||
"typecheck": "tsc --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@eva-design/eva": "2.0.0",
|
||||
|
@ -58,6 +59,11 @@
|
|||
"@testing-library/jest-native": "^4.0.1",
|
||||
"@testing-library/react-hooks": "^5.1.0",
|
||||
"@testing-library/react-native": "7.1.0",
|
||||
"@types/jest": "^26.0.22",
|
||||
"@types/jsuri": "^1.3.30",
|
||||
"@types/luxon": "^1.26.2",
|
||||
"@types/markdown-it": "^12.0.1",
|
||||
"@types/react-native": "^0.64.1",
|
||||
"@ui-kitten/metro-config": "5.0.0",
|
||||
"babel-jest": "^26.6.3",
|
||||
"eslint": "^7.21.0",
|
||||
|
@ -69,7 +75,8 @@
|
|||
"metro-react-native-babel-preset": "^0.65.2",
|
||||
"mockdate": "^3.0.2",
|
||||
"prettier": "^2.2.1",
|
||||
"react-test-renderer": "16.13.1"
|
||||
"react-test-renderer": "16.13.1",
|
||||
"typescript": "^4.2.3"
|
||||
},
|
||||
"private": true,
|
||||
"resolutions": {
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "commonjs",
|
||||
"lib": [ "es2019"],
|
||||
"allowJs": true,
|
||||
"jsx": "react-native",
|
||||
"noEmit": true,
|
||||
"isolatedModules": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": false,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules", "babel.config.js", "metro.config.js", "jest.config.js"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
declare module 'rn-actionsheet-module'
|
|
@ -1715,6 +1715,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/he/-/he-1.1.1.tgz#19e14033c4ee8f1a702c74dcc6182664839ac2b7"
|
||||
integrity sha512-jpzrsR1ns0n3kyWt92QfOUQhIuJGQ9+QGa7M62rO6toe98woQjnsnzjdMtsQXCdvjjmqjS2ZBCC7xKw0cdzU+Q==
|
||||
|
||||
"@types/highlight.js@^9.7.0":
|
||||
version "9.12.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.12.4.tgz#8c3496bd1b50cc04aeefd691140aa571d4dbfa34"
|
||||
integrity sha512-t2szdkwmg2JJyuCM20e8kR2X59WCE5Zkl4bzm1u1Oukjm79zpbiAv+QjnwLnuuV0WHEcX2NgUItu0pAMKuOPww==
|
||||
|
||||
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
|
||||
|
@ -1742,16 +1747,53 @@
|
|||
dependencies:
|
||||
"@types/istanbul-lib-report" "*"
|
||||
|
||||
"@types/jest@^26.0.22":
|
||||
version "26.0.22"
|
||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.22.tgz#8308a1debdf1b807aa47be2838acdcd91e88fbe6"
|
||||
integrity sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw==
|
||||
dependencies:
|
||||
jest-diff "^26.0.0"
|
||||
pretty-format "^26.0.0"
|
||||
|
||||
"@types/json-schema@^7.0.3":
|
||||
version "7.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
|
||||
integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
|
||||
|
||||
"@types/jsuri@^1.3.30":
|
||||
version "1.3.30"
|
||||
resolved "https://registry.yarnpkg.com/@types/jsuri/-/jsuri-1.3.30.tgz#2feb9768b43fe0494a60d39d78172bd31a4e82a9"
|
||||
integrity sha512-n3RoOl8LHzDX7gmgxVWU09cMiUn1chW0J70N6oOptOoLt8OWvyq8lpW9Mj4xvzHqJEiOMR+J1okPSL1XeBz1uw==
|
||||
|
||||
"@types/linkify-it@*":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-3.0.1.tgz#4d26a9efe3aa2caf829234ec5a39580fc88b6001"
|
||||
integrity sha512-pQv3Sygwxxh6jYQzXaiyWDAHevJqWtqDUv6t11Sa9CPGiXny66II7Pl6PR8QO5OVysD6HYOkHMeBgIjLnk9SkQ==
|
||||
|
||||
"@types/lodash@^4.14.165":
|
||||
version "4.14.168"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008"
|
||||
integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==
|
||||
|
||||
"@types/luxon@^1.26.2":
|
||||
version "1.26.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-1.26.2.tgz#656c24c1af3d41b8854700dc94ed556b9b6ce2f8"
|
||||
integrity sha512-2pvzy4LuxBMBBLAbml6PDcJPiIeZQ0Hqj3PE31IxkNI250qeoRMDovTrHXeDkIL4auvtarSdpTkLHs+st43EYQ==
|
||||
|
||||
"@types/markdown-it@^12.0.1":
|
||||
version "12.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-12.0.1.tgz#8391e19fea4796ff863edda55800c7e669beb358"
|
||||
integrity sha512-mHfT8j/XkPb1uLEfs0/C3se6nd+webC2kcqcy8tgcVr0GDEONv/xaQzAN+aQvkxQXk/jC0Q6mPS+0xhFwRF35g==
|
||||
dependencies:
|
||||
"@types/highlight.js" "^9.7.0"
|
||||
"@types/linkify-it" "*"
|
||||
"@types/mdurl" "*"
|
||||
|
||||
"@types/mdurl@*":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9"
|
||||
integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==
|
||||
|
||||
"@types/node@*":
|
||||
version "14.14.31"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055"
|
||||
|
@ -1789,6 +1831,13 @@
|
|||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-native@^0.64.1":
|
||||
version "0.64.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.1.tgz#03c897d9567c357922d06d4ed159354c81a3fe5c"
|
||||
integrity sha512-5VUk12LTxawpcRLbzzTtbcFVHCw8ro7a9dF2OsJSwNiMZAgziyqZbAv1Sw1TgoFlfX0rOxXv/tMMJ1r1HVZ8Og==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-test-renderer@>=16.9.0":
|
||||
version "17.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-17.0.1.tgz#3120f7d1c157fba9df0118dae20cb0297ee0e06b"
|
||||
|
@ -5082,7 +5131,7 @@ jest-diff@^25.5.0:
|
|||
jest-get-type "^25.2.6"
|
||||
pretty-format "^25.5.0"
|
||||
|
||||
jest-diff@^26.6.2:
|
||||
jest-diff@^26.0.0, jest-diff@^26.6.2:
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394"
|
||||
integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==
|
||||
|
@ -7318,7 +7367,7 @@ pretty-format@^25.1.0, pretty-format@^25.2.0, pretty-format@^25.5.0:
|
|||
ansi-styles "^4.0.0"
|
||||
react-is "^16.12.0"
|
||||
|
||||
pretty-format@^26.0.1, pretty-format@^26.6.2:
|
||||
pretty-format@^26.0.0, pretty-format@^26.0.1, pretty-format@^26.6.2:
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93"
|
||||
integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==
|
||||
|
@ -8883,6 +8932,11 @@ typedarray@^0.0.6:
|
|||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||
|
||||
typescript@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
|
||||
integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
|
||||
|
||||
ua-parser-js@^0.7.18:
|
||||
version "0.7.24"
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.24.tgz#8d3ecea46ed4f1f1d63ec25f17d8568105dc027c"
|
||||
|
|
Loading…
Reference in New Issue