Merge pull request #514 from kolplattformen/feature/reload-data

Feature/reload data
This commit is contained in:
Viktor Sarström 2021-12-03 08:48:28 +01:00 committed by GitHub
commit 7bee08a550
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 180 additions and 64 deletions

View File

@ -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

View File

@ -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,
},
})

View File

@ -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,

View File

@ -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}
/>
)}
/>

View File

@ -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} />
}
/>
)
}

View File

@ -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')

View File

@ -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} />
}
/>
)
}

View File

@ -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}

View File

@ -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'}
/>
}
/>
)
}

View File

@ -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} />
}
/>
)
}

View File

@ -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 = (

View File

@ -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!",