Merge pull request #514 from kolplattformen/feature/reload-data
Feature/reload data
This commit is contained in:
commit
7bee08a550
|
@ -19,7 +19,7 @@ import { translations } from './utils/translation'
|
|||
const reporter: Reporter | undefined = __DEV__
|
||||
? {
|
||||
log: (message: string) => console.log(message),
|
||||
error: (error: Error, label?: string) => console.error(label, error),
|
||||
error: (error: Error, label?: string) => console.log(label, error),
|
||||
}
|
||||
: undefined
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { CalendarItem } from '@skolplattformen/api'
|
||||
import { useCalendar } from '@skolplattformen/hooks'
|
||||
import { CalendarItem } from '@skolplattformen/api'
|
||||
import {
|
||||
Divider,
|
||||
List,
|
||||
|
@ -10,8 +10,9 @@ import {
|
|||
} from '@ui-kitten/components'
|
||||
import moment from 'moment'
|
||||
import React from 'react'
|
||||
import { ListRenderItemInfo, View } from 'react-native'
|
||||
import { Typography } from '../styles'
|
||||
import { ListRenderItemInfo, RefreshControl, View } from 'react-native'
|
||||
import { Layout as LayoutStyle, Sizing, Typography } from '../styles'
|
||||
import { translate } from '../utils/translation'
|
||||
import { useChild } from './childContext.component'
|
||||
import { CalendarOutlineIcon } from './icon.component'
|
||||
import { SaveToCalendar } from './saveToCalendar.component'
|
||||
|
@ -19,7 +20,7 @@ import { Week } from './week.component'
|
|||
|
||||
export const Calendar = () => {
|
||||
const child = useChild()
|
||||
const { data } = useCalendar(child)
|
||||
const { data, status, reload } = useCalendar(child)
|
||||
const styles = useStyleSheet(themedStyles)
|
||||
|
||||
const formatStartDate = (startDate: moment.MomentInput) => {
|
||||
|
@ -28,37 +29,55 @@ export const Calendar = () => {
|
|||
'll'
|
||||
)} • ${date.fromNow()}`
|
||||
|
||||
// Hack to remove yarn if it is this year
|
||||
// Hack to remove year if it is this year
|
||||
const currentYear = moment().year().toString(10)
|
||||
return output.replace(currentYear, '')
|
||||
}
|
||||
|
||||
const sortedData = () => {
|
||||
if (!data) return []
|
||||
|
||||
return data.sort((a, b) =>
|
||||
a.startDate && b.startDate ? a.startDate.localeCompare(b.startDate) : 0
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Week child={child} />
|
||||
{data && data.length > 0 && (
|
||||
<List
|
||||
data={data.sort((a, b) =>
|
||||
a.startDate && b.startDate
|
||||
? a.startDate.localeCompare(b.startDate)
|
||||
: 0
|
||||
)}
|
||||
ItemSeparatorComponent={Divider}
|
||||
renderItem={({ item }: ListRenderItemInfo<CalendarItem>) => (
|
||||
<ListItem
|
||||
disabled={true}
|
||||
title={`${item.title}`}
|
||||
description={(props) => (
|
||||
<Text style={[props?.style, styles.description]}>
|
||||
{formatStartDate(item.startDate)}
|
||||
</Text>
|
||||
)}
|
||||
accessoryLeft={CalendarOutlineIcon}
|
||||
accessoryRight={() => <SaveToCalendar event={item} />}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<List
|
||||
data={sortedData()}
|
||||
ItemSeparatorComponent={Divider}
|
||||
ListEmptyComponent={
|
||||
<View style={styles.emptyState}>
|
||||
<Text style={styles.emptyStateHeadline} category="h6">
|
||||
{translate('calender.emptyHeadline')}
|
||||
</Text>
|
||||
<Text style={styles.emptyStateDescription}>
|
||||
{translate('calender.emptyText')}
|
||||
</Text>
|
||||
</View>
|
||||
}
|
||||
renderItem={({ item }: ListRenderItemInfo<CalendarItem>) => (
|
||||
<ListItem
|
||||
disabled={true}
|
||||
title={`${item.title}`}
|
||||
description={(props) => (
|
||||
<Text style={[props?.style, styles.description]}>
|
||||
{formatStartDate(item.startDate)}
|
||||
</Text>
|
||||
)}
|
||||
accessoryLeft={CalendarOutlineIcon}
|
||||
accessoryRight={() => <SaveToCalendar event={item} />}
|
||||
/>
|
||||
)}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={status === 'loading'}
|
||||
onRefresh={reload}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
@ -73,4 +92,18 @@ const themedStyles = StyleService.create({
|
|||
...Typography.fontSize.xs,
|
||||
color: 'text-hint-color',
|
||||
},
|
||||
emptyState: {
|
||||
...LayoutStyle.center,
|
||||
...LayoutStyle.flex.full,
|
||||
},
|
||||
emptyStateHeadline: {
|
||||
...Typography.align.center,
|
||||
margin: Sizing.t4,
|
||||
},
|
||||
emptyStateDescription: {
|
||||
...Typography.align.center,
|
||||
lineHeight: 21,
|
||||
paddingHorizontal: Sizing.t3,
|
||||
margin: Sizing.t4,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
useStyleSheet,
|
||||
} from '@ui-kitten/components'
|
||||
import moment from 'moment'
|
||||
import React from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import { TouchableOpacity, useColorScheme, View } from 'react-native'
|
||||
import { useTranslation } from '../hooks/useTranslation'
|
||||
import { Colors, Layout, Sizing } from '../styles'
|
||||
|
@ -30,13 +30,18 @@ import { StudentAvatar } from './studentAvatar.component'
|
|||
interface ChildListItemProps {
|
||||
child: Child
|
||||
color: string
|
||||
updated: string
|
||||
}
|
||||
type ChildListItemNavigationProp = StackNavigationProp<
|
||||
RootStackParamList,
|
||||
'Children'
|
||||
>
|
||||
|
||||
export const ChildListItem = ({ child, color }: ChildListItemProps) => {
|
||||
export const ChildListItem = ({
|
||||
child,
|
||||
color,
|
||||
updated,
|
||||
}: ChildListItemProps) => {
|
||||
// Forces rerender when child.id changes
|
||||
React.useEffect(() => {
|
||||
// noop
|
||||
|
@ -44,17 +49,38 @@ export const ChildListItem = ({ child, color }: ChildListItemProps) => {
|
|||
|
||||
const navigation = useNavigation<ChildListItemNavigationProp>()
|
||||
const { t } = useTranslation()
|
||||
const { data: notifications } = useNotifications(child)
|
||||
const { data: news } = useNews(child)
|
||||
const { data: classmates } = useClassmates(child)
|
||||
const { data: calendar } = useCalendar(child)
|
||||
const { data: menu } = useMenu(child)
|
||||
const { data: schedule } = useSchedule(
|
||||
const { data: notifications, reload: notificationsReload } =
|
||||
useNotifications(child)
|
||||
const { data: news, status: newsStatus, reload: newsReload } = useNews(child)
|
||||
const { data: classmates, reload: classmatesReload } = useClassmates(child)
|
||||
const { data: calendar, reload: calendarReload } = useCalendar(child)
|
||||
const { data: menu, reload: menuReload } = useMenu(child)
|
||||
const { data: schedule, reload: scheduleReload } = useSchedule(
|
||||
child,
|
||||
moment().toISOString(),
|
||||
moment().add(7, 'days').toISOString()
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
// Do not refresh if updated is empty (first render of component)
|
||||
if (updated === '') return
|
||||
|
||||
console.log('Reload', child.name, updated)
|
||||
|
||||
newsReload()
|
||||
classmatesReload()
|
||||
notificationsReload()
|
||||
calendarReload()
|
||||
menuReload()
|
||||
scheduleReload()
|
||||
|
||||
// Without eslint-disable below we get into a forever loop
|
||||
// because the function pointers to reload functions change on every reload.
|
||||
// I do not know a workaround for this.
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [updated])
|
||||
|
||||
const notificationsThisWeek = notifications.filter(
|
||||
({ dateCreated, dateModified }) => {
|
||||
const date = dateModified || dateCreated
|
||||
|
@ -168,7 +194,6 @@ export const ChildListItem = ({ child, color }: ChildListItemProps) => {
|
|||
{t('news.noNewNewsItemsThisWeek')}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{!menu[moment().isoWeekday() - 1] ? null : (
|
||||
<>
|
||||
<Text category="c2" style={styles.label}>
|
||||
|
@ -177,7 +202,7 @@ export const ChildListItem = ({ child, color }: ChildListItemProps) => {
|
|||
<Text>{menu[moment().isoWeekday() - 1]?.description}</Text>
|
||||
</>
|
||||
)}
|
||||
<View style={styles.itemFooterAbsence}>
|
||||
<View style={styles.itemFooter}>
|
||||
<Button
|
||||
accessible
|
||||
accessibilityRole="button"
|
||||
|
@ -232,15 +257,16 @@ const themeStyles = StyleService.create({
|
|||
},
|
||||
itemFooter: {
|
||||
...Layout.flex.row,
|
||||
marginTop: Sizing.t4,
|
||||
},
|
||||
itemFooterAbsence: {
|
||||
...Layout.mainAxis.flexStart,
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'flex-end',
|
||||
marginTop: Sizing.t4,
|
||||
},
|
||||
absenceButton: {
|
||||
marginLeft: -20,
|
||||
},
|
||||
itemFooterSpinner: {
|
||||
alignSelf: 'flex-end',
|
||||
},
|
||||
item: {
|
||||
marginRight: 12,
|
||||
paddingHorizontal: 2,
|
||||
|
|
|
@ -10,7 +10,8 @@ import {
|
|||
TopNavigationAction,
|
||||
useStyleSheet,
|
||||
} from '@ui-kitten/components'
|
||||
import React, { useCallback, useEffect } from 'react'
|
||||
import moment from 'moment'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import {
|
||||
Image,
|
||||
ImageStyle,
|
||||
|
@ -24,7 +25,7 @@ import AppStorage from '../services/appStorage'
|
|||
import { Colors, Layout as LayoutStyle, Sizing, Typography } from '../styles'
|
||||
import { translate } from '../utils/translation'
|
||||
import { ChildListItem } from './childListItem.component'
|
||||
import { SettingsIcon } from './icon.component'
|
||||
import { SettingsIcon, RefreshIcon } from './icon.component'
|
||||
|
||||
const colors = ['primary', 'success', 'info', 'warning', 'danger']
|
||||
|
||||
|
@ -45,9 +46,12 @@ export const Children = () => {
|
|||
|
||||
const { api } = useApi()
|
||||
const { data: childList, status, reload } = useChildList()
|
||||
const reloadChildren = () => {
|
||||
const reloadChildren = useCallback(() => {
|
||||
reload()
|
||||
}
|
||||
setUpdated(moment().toISOString())
|
||||
}, [reload])
|
||||
|
||||
const [updatedAt, setUpdated] = useState('')
|
||||
|
||||
const logout = useCallback(() => {
|
||||
AppStorage.clearTemporaryItems().then(() => api.logout())
|
||||
|
@ -63,8 +67,18 @@ export const Children = () => {
|
|||
/>
|
||||
)
|
||||
},
|
||||
headerRight: () => {
|
||||
return (
|
||||
<TopNavigationAction
|
||||
icon={RefreshIcon}
|
||||
onPress={() => reloadChildren()}
|
||||
accessibilityHint="Reload"
|
||||
accessibilityLabel="Reload"
|
||||
/>
|
||||
)
|
||||
},
|
||||
})
|
||||
}, [navigation])
|
||||
}, [navigation, reloadChildren])
|
||||
|
||||
// We need to skip safe area view here, due to the reason that it's adding a white border
|
||||
// when this view is actually lightgrey. Taking the padding top value from the use inset hook.
|
||||
|
@ -93,6 +107,7 @@ export const Children = () => {
|
|||
child={child}
|
||||
color={colors[index % colors.length]}
|
||||
key={child.id}
|
||||
updated={updatedAt}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
Text,
|
||||
} from '@ui-kitten/components'
|
||||
import React from 'react'
|
||||
import { ListRenderItemInfo, StyleSheet } from 'react-native'
|
||||
import { ListRenderItemInfo, RefreshControl, StyleSheet } from 'react-native'
|
||||
import { fullName, guardians, sortByFirstName } from '../utils/peopleHelpers'
|
||||
import { translate } from '../utils/translation'
|
||||
import { useChild } from './childContext.component'
|
||||
|
@ -22,7 +22,7 @@ interface ClassmatesProps {
|
|||
export const Classmates = () => {
|
||||
const child = useChild()
|
||||
|
||||
const { data } = useClassmates(child)
|
||||
const { data, status, reload } = useClassmates(child)
|
||||
const renderItemIcon = (props: IconProps) => (
|
||||
<Icon {...props} name="people-outline" />
|
||||
)
|
||||
|
@ -60,6 +60,9 @@ export const Classmates = () => {
|
|||
}
|
||||
renderItem={renderItem}
|
||||
contentContainerStyle={styles.contentContainer}
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={status === 'loading'} onRefresh={reload} />
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -32,3 +32,4 @@ export const ClipboardIcon = uiIcon('clipboard-outline')
|
|||
export const RightArrowIcon = uiIcon('arrow-ios-forward-outline')
|
||||
export const QuestionMarkIcon = uiIcon('question-mark')
|
||||
export const AwardIcon = uiIcon('award')
|
||||
export const RefreshIcon = uiIcon('refresh')
|
||||
|
|
|
@ -9,7 +9,13 @@ import {
|
|||
} from '@ui-kitten/components'
|
||||
import 'moment/locale/sv'
|
||||
import React from 'react'
|
||||
import { Image, ImageStyle, ListRenderItemInfo, View } from 'react-native'
|
||||
import {
|
||||
Image,
|
||||
ImageStyle,
|
||||
ListRenderItemInfo,
|
||||
RefreshControl,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import { Layout as LayoutStyle, Sizing, Typography } from '../styles'
|
||||
import { translate } from '../utils/translation'
|
||||
import { useChild } from './childContext.component'
|
||||
|
@ -18,7 +24,7 @@ import { MenuListItem } from './menuListItem.component'
|
|||
export const Menu = () => {
|
||||
const styles = useStyleSheet(themedStyles)
|
||||
const child = useChild()
|
||||
const { data } = useMenu(child)
|
||||
const { data, status, reload } = useMenu(child)
|
||||
|
||||
return (
|
||||
<List
|
||||
|
@ -42,6 +48,9 @@ export const Menu = () => {
|
|||
<MenuListItem key={item.title} item={item} />
|
||||
)}
|
||||
style={styles.container}
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={status === 'loading'} onRefresh={reload} />
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,13 @@ import { StyleService, Text, useStyleSheet } from '@ui-kitten/components'
|
|||
import moment from 'moment'
|
||||
import 'moment/locale/sv'
|
||||
import React from 'react'
|
||||
import { Dimensions, ImageStyle, ScrollView, View } from 'react-native'
|
||||
import {
|
||||
Dimensions,
|
||||
ImageStyle,
|
||||
RefreshControl,
|
||||
ScrollView,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import { NativeStackNavigationOptions } from 'react-native-screens/native-stack'
|
||||
import { defaultStackStyling } from '../design/navigationThemes'
|
||||
import { Layout, Sizing, Typography } from '../styles'
|
||||
|
@ -46,7 +52,7 @@ export const newsItemRouteOptions =
|
|||
|
||||
export const NewsItem = ({ route }: NewsItemProps) => {
|
||||
const { newsItem, child } = route.params
|
||||
const { data } = useNewsDetails(child, newsItem)
|
||||
const { data, status, reload } = useNewsDetails(child, newsItem)
|
||||
const styles = useStyleSheet(themedStyles)
|
||||
const stylesMarkdown = useStyleSheet(themedStylesMarkdown)
|
||||
|
||||
|
@ -54,6 +60,9 @@ export const NewsItem = ({ route }: NewsItemProps) => {
|
|||
<ScrollView
|
||||
contentContainerStyle={styles.article}
|
||||
style={styles.scrollView}
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={status === 'loading'} onRefresh={reload} />
|
||||
}
|
||||
>
|
||||
<Text maxFontSizeMultiplier={2} style={styles.title}>
|
||||
{newsItem.header}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useNews } from '@skolplattformen/hooks'
|
||||
import { Input, List, StyleService, useStyleSheet } from '@ui-kitten/components'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import { TouchableOpacity, View } from 'react-native'
|
||||
import { TouchableOpacity, View, RefreshControl } from 'react-native'
|
||||
import { Sizing } from '../styles'
|
||||
import {
|
||||
renderSearchResultPreview,
|
||||
|
@ -15,7 +15,7 @@ import { NewsListItem } from './newsListItem.component'
|
|||
export const NewsList = () => {
|
||||
const styles = useStyleSheet(themedStyles)
|
||||
const child = useChild()
|
||||
const { data } = useNews(child)
|
||||
const { data, status, reload } = useNews(child)
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const searchResults = useNewsListSearchResults(searchQuery)
|
||||
|
@ -62,6 +62,13 @@ export const NewsList = () => {
|
|||
{renderSearchResultPreview(searchResult)}
|
||||
</NewsListItem>
|
||||
)}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={status === 'loading'}
|
||||
onRefresh={reload}
|
||||
tintColor={'color-basic-100'}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -74,6 +81,13 @@ export const NewsList = () => {
|
|||
data={data}
|
||||
ListHeaderComponent={header}
|
||||
renderItem={({ item }) => <NewsListItem key={item.id} item={item} />}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={status === 'loading'}
|
||||
onRefresh={reload}
|
||||
tintColor={'color-basic-100'}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { useNotifications } from '@skolplattformen/hooks'
|
||||
import { List, StyleService, useStyleSheet } from '@ui-kitten/components'
|
||||
import React from 'react'
|
||||
import { RefreshControl } from 'react-native'
|
||||
import { Sizing } from '../styles'
|
||||
import { useChild } from './childContext.component'
|
||||
import { Notification } from './notification.component'
|
||||
|
@ -8,7 +9,7 @@ import { Notification } from './notification.component'
|
|||
export const NotificationsList = () => {
|
||||
const styles = useStyleSheet(themedStyles)
|
||||
const child = useChild()
|
||||
const { data } = useNotifications(child)
|
||||
const { data, status, reload } = useNotifications(child)
|
||||
|
||||
return (
|
||||
<List
|
||||
|
@ -18,6 +19,9 @@ export const NotificationsList = () => {
|
|||
renderItem={(info) => (
|
||||
<Notification key={info.item.id} item={info.item} />
|
||||
)}
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={status === 'loading'} onRefresh={reload} />
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -573,9 +573,9 @@
|
|||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-frameworks.sh",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/double-conversion/double-conversion.framework/double-conversion",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL/OpenSSL.framework/OpenSSL",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes/hermes.framework/hermes",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-DoubleConversion/double-conversion.framework/double-conversion",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/hermes.framework/hermes",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
|
@ -595,9 +595,9 @@
|
|||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-app-appTests/Pods-app-appTests-frameworks.sh",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/double-conversion/double-conversion.framework/double-conversion",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL/OpenSSL.framework/OpenSSL",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes/hermes.framework/hermes",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-DoubleConversion/double-conversion.framework/double-conversion",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/hermes.framework/hermes",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
|
|
|
@ -58,7 +58,9 @@
|
|||
"saveToCalender": "Spara till kalender",
|
||||
"saveToCalenderError": "Något gick fel",
|
||||
"saveToCalenderSuccess": "✔️ Sparad till kalender",
|
||||
"showCalenderActions": "Visa kalenderfunktioner"
|
||||
"showCalenderActions": "Visa kalenderfunktioner",
|
||||
"emptyHeadline": "Det ser lite tomt ut i kalendern",
|
||||
"emptyText": "Hittade ingenting att visa"
|
||||
},
|
||||
"children": {
|
||||
"loadingErrorHeading": "Hoppsan!",
|
||||
|
|
Loading…
Reference in New Issue