diff --git a/index.js b/index.js index fd095258..f84bbfc5 100644 --- a/index.js +++ b/index.js @@ -12,6 +12,8 @@ import { setNativeExceptionHandler, } from 'react-native-exception-handler' +import '@/services/system/skeleton-data.service' + const errorHandler = (e, isFatal) => { if (isFatal) { Alert.alert( diff --git a/ios/taskme.xcodeproj/project.pbxproj b/ios/taskme.xcodeproj/project.pbxproj index 8522bb79..34140c0b 100644 --- a/ios/taskme.xcodeproj/project.pbxproj +++ b/ios/taskme.xcodeproj/project.pbxproj @@ -764,7 +764,7 @@ CODE_SIGN_ENTITLEMENTS = taskme/taskme.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 105; + CURRENT_PROJECT_VERSION = 106; DEVELOPMENT_TEAM = HQ3J3TDPR2; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; @@ -802,7 +802,7 @@ CODE_SIGN_ENTITLEMENTS = taskme/taskme.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 105; + CURRENT_PROJECT_VERSION = 106; DEVELOPMENT_TEAM = HQ3J3TDPR2; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; diff --git a/package-lock.json b/package-lock.json index dd82ba0d..9d0e794b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@react-native-async-storage/async-storage": "^1.15.8", "@react-native-community/cameraroll": "^4.1.2", "@react-native-community/clipboard": "^1.5.1", + "@react-native-community/datetimepicker": "^6.1.3", "@react-native-community/netinfo": "^9.0.0", "@react-native-community/push-notification-ios": "^1.10.1", "@react-native-picker/picker": "^2.4.1", @@ -27,6 +28,7 @@ "axios": "^0.21.1", "buffer": "^6.0.3", "cachios": "^3.0.0", + "deprecated-react-native-prop-types": "^2.3.0", "events": "^3.3.0", "jet-tools": "^1.1.0", "lodash": "^4.17.21", @@ -2962,7 +2964,6 @@ "version": "6.1.3", "resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-6.1.3.tgz", "integrity": "sha512-3e20BPQy8QiQeIGJL4zoJaRsGWB31hl9i7oTZECWxnK6nVUeqGt/G+JRJlJinmqL6z4WePHC6RSUMgpC64OOwg==", - "peer": true, "dependencies": { "invariant": "^2.2.4" } @@ -5667,7 +5668,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-2.3.0.tgz", "integrity": "sha512-pWD0voFtNYxrVqvBMYf5gq3NA2GCpfodS1yNynTPc93AYA/KEMGeWDqqeUB6R2Z9ZofVhks2aeJXiuQqKNpesA==", - "peer": true, "dependencies": { "@react-native/normalize-color": "*", "invariant": "*", @@ -17883,7 +17883,6 @@ "version": "6.1.3", "resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-6.1.3.tgz", "integrity": "sha512-3e20BPQy8QiQeIGJL4zoJaRsGWB31hl9i7oTZECWxnK6nVUeqGt/G+JRJlJinmqL6z4WePHC6RSUMgpC64OOwg==", - "peer": true, "requires": { "invariant": "^2.2.4" } @@ -19994,7 +19993,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-2.3.0.tgz", "integrity": "sha512-pWD0voFtNYxrVqvBMYf5gq3NA2GCpfodS1yNynTPc93AYA/KEMGeWDqqeUB6R2Z9ZofVhks2aeJXiuQqKNpesA==", - "peer": true, "requires": { "@react-native/normalize-color": "*", "invariant": "*", diff --git a/src/api/tasks/transform.ts b/src/api/tasks/transform.ts index 283b3cb2..bf8a46d7 100644 --- a/src/api/tasks/transform.ts +++ b/src/api/tasks/transform.ts @@ -1,4 +1,9 @@ -import { createFullName, IFullTaskInfo, IShortUser } from '@/shared' +import { + createFullName, + IFullTaskInfo, + IShortUser, + ITaskPreview, +} from '@/shared' import { IFetchTaskDetailsResponse, ITaskExecutorResponse, @@ -77,22 +82,22 @@ export const transformTaskDetails = ( export const transformTasksInList = ( items: (ITaskPreviewResponse | ITaskWithExecutorPreviewResponse)[], executor?: IShortUser, -) => { +): ITaskPreview[] => { const transformedItems = items.map(item => { const taskExecutor = executor ? executor : { - id: (item as ITaskWithExecutorPreviewResponse).executor - .userId, - fullName: createFullName( - (item as ITaskWithExecutorPreviewResponse).executor - .firstName, - (item as ITaskWithExecutorPreviewResponse).executor - .lastName, - ), - avatarUrl: (item as ITaskWithExecutorPreviewResponse) - .executor.avatarUrl, - } + id: (item as ITaskWithExecutorPreviewResponse).executor + .userId, + fullName: createFullName( + (item as ITaskWithExecutorPreviewResponse).executor + .firstName, + (item as ITaskWithExecutorPreviewResponse).executor + .lastName, + ), + avatarUrl: (item as ITaskWithExecutorPreviewResponse) + .executor.avatarUrl, + } const transformedItem = { id: item.id, @@ -106,7 +111,7 @@ export const transformTasksInList = ( isFavorite: item.isFavorite, hasUnreadComments: item.hasUnreadComments, hasAttachments: item.hasDocuments, - events: item.events, + events: item.events as any, doneDate: item.doneDate, } diff --git a/src/modules/chats/components/chats-list.component.tsx b/src/modules/chats/components/chats-list.component.tsx index b3e171ba..c20ec1de 100644 --- a/src/modules/chats/components/chats-list.component.tsx +++ b/src/modules/chats/components/chats-list.component.tsx @@ -16,6 +16,7 @@ import { ActivityIndicator, FlatList, StyleSheet, + Text, View, ViewStyle, } from 'react-native' @@ -52,39 +53,29 @@ export const ChatsList: FC = ({ }) => { const { styles, theme } = useTheme(createStyles) - const itemToRender = ({ item, index }: any) => { - const previewUrl = - item?.type === ChatType.Group - ? item?.previewUrl - : item?.chatMembers[0]?.user?.avatarUrl - - const name = - item?.type === ChatType.Group - ? item.name - : createFullName( - item?.chatMembers[0]?.user?.firstName, - item?.chatMembers[0]?.user?.lastName, - ) - - return ( - onPressItem(item)} - onPressActionBtn={onPressActionBtn} - /> - ) - } + const itemToRender = useCallback( + ({ item, index }: any) => { + return ( + onPressItem(item)} + onPressActionBtn={onPressActionBtn} + /> + ) + }, + [onPressItem, onPressActionBtn], + ) const renderFooter = useCallback(() => { if (!isLoading && isLoadingNext) diff --git a/src/modules/chats/configs/chat-card-buttons.config.ts b/src/modules/chats/configs/chat-card-buttons.config.ts index f0ed6f95..d8adbb7e 100644 --- a/src/modules/chats/configs/chat-card-buttons.config.ts +++ b/src/modules/chats/configs/chat-card-buttons.config.ts @@ -13,7 +13,7 @@ type BtnsConfigT = { interface configProps { id: number theme: PartialTheme - unreadMessagesCount: number + unreadMessagesCount?: number onPress: ( actionType: ChatCardActionEnum, chatId: number, diff --git a/src/modules/chats/hooks/use-chats-list.hook.ts b/src/modules/chats/hooks/use-chats-list.hook.ts index ddeae23c..4beb3ba3 100644 --- a/src/modules/chats/hooks/use-chats-list.hook.ts +++ b/src/modules/chats/hooks/use-chats-list.hook.ts @@ -4,8 +4,10 @@ import { appEvents, ChatMemberRole, ChatType, + createFullName, IChat, RouteKey, + StorageKey, useEventsListener, useFlatList, useNav, @@ -31,6 +33,7 @@ import { import { showUknowError } from '@/shared/helpers/alert.helper' import { getChatIdFromMessages, isChatInList } from '../helpers' +import { InteractionManager } from 'react-native' export const useChatList = () => { const nav = useNav() @@ -38,6 +41,25 @@ export const useChatList = () => { const accountId = useSelector(selectId) const [searchString, setSearchVal] = useState(null) + const serrializatorItems = useCallback( + (items: any[]) => + items.map(item => ({ + ...item, + name: + item?.type === ChatType.Group + ? item.name + : createFullName( + item?.chatMembers[0]?.user?.firstName, + item?.chatMembers[0]?.user?.lastName, + ), + previewUrl: + item?.type === ChatType.Group + ? item?.previewUrl + : item?.chatMembers[0]?.user?.avatarUrl, + })), + [], + ) + const { items: chats, loadMore, @@ -51,8 +73,34 @@ export const useChatList = () => { needInit: true, clearWhenReload: false, limit: 10, + serrializatorItems: serrializatorItems, }) + useEffect(() => { + InteractionManager.runAfterInteractions(() => { + resetFlatList() + }) + }, []) + + // const cacheItems = async () => { + // const toSave = _.slice(chats, 0, 11) + // await storageService.set(StorageKey.Chats, JSON.stringify(toSave)) + // } + + // const getCache = async () => { + // const itemsJson = await storageService.get(StorageKey.Chats) + // if (!itemsJson) return + // _setItems(JSON.parse(itemsJson)) + // } + + // useEffect(() => { + // if (!isLoading) cacheItems() + // }, [isLoading]) + + // useEffect(() => { + // if (cachedItems) _setItems(cachedItems) + // }, []) + useEffect(() => { if (searchString !== null) setLoadParams({ searchString }) }, [searchString]) @@ -360,7 +408,7 @@ export const useChatList = () => { resetFlatList: resetFlatList, setSearchVal, handleChatAction, - isLoading, - isLoadingNext, + isLoading: false, + isLoadingNext: false, } } diff --git a/src/modules/chats/screens/chats.screen.tsx b/src/modules/chats/screens/chats.screen.tsx index 6841685b..02c2a06d 100644 --- a/src/modules/chats/screens/chats.screen.tsx +++ b/src/modules/chats/screens/chats.screen.tsx @@ -1,11 +1,17 @@ import React, { FC } from 'react' -import { $size, IRouteParams, RouteKey, ScreenLayout } from '@/shared' +import { + $size, + IRouteParams, + PrimaryHeader, + RouteKey, + ScreenLayout, +} from '@/shared' import { useTheme } from '@/shared/hooks/use-theme.hook' -import { Platform, StyleSheet } from 'react-native' +import { StyleSheet } from 'react-native' import { HeaderRightBtn } from '../atoms' import { SmartChatsList } from '../smart-components' -interface IProps extends IRouteParams { } +interface IProps extends IRouteParams {} export const ChatsScreen: FC = ({ navigation }) => { const { styles } = useTheme(createStyles) @@ -19,17 +25,16 @@ export const ChatsScreen: FC = ({ navigation }) => { useChatBtnColors: true, }) + const props = { + title: 'Бесіди', + rightComponent: , + goBack: navigation.goBack, + style: styles.header, + } return ( ( - - ), - goBack: navigation.goBack, - style: styles.header, - }}> + headerComponent={}> ) diff --git a/src/modules/chats/smart-components/chats-list.smart-component.tsx b/src/modules/chats/smart-components/chats-list.smart-component.tsx index be4e68c5..91b6ccb7 100644 --- a/src/modules/chats/smart-components/chats-list.smart-component.tsx +++ b/src/modules/chats/smart-components/chats-list.smart-component.tsx @@ -1,21 +1,37 @@ -import { $size, appEvents, IChat, RouteKey } from '@/shared' +import { storageService } from '@/services/system' +import { $size, appEvents, IChat, RouteKey, StorageKey } from '@/shared' import { SearchForm } from '@/shared/components/forms' import { useTheme } from '@/shared/hooks/use-theme.hook' import { SelectChat } from '@/store/chats' import { simpleDispatch } from '@/store/store-helpers' import { useNavigation } from '@react-navigation/native' import { StackNavigationProp } from '@react-navigation/stack' -import React, { FC } from 'react' +import React, { FC, useEffect, useState } from 'react' import { Platform, StyleSheet } from 'react-native' import { ChatsList } from '../components/chats-list.component' import { useChatList } from '../hooks' type TConversationNav = StackNavigationProp<{ [RouteKey.Conversation] }> +let cachedItems = [] + +// const getCache = async () => { +// const itemsJson = await storageService.get(StorageKey.Chats) +// if (!itemsJson) return +// cachedItems = JSON.parse(itemsJson) +// } + +// getCache() + export const SmartChatsList: FC = () => { const { styles } = useTheme(createStyles) const { navigate } = useNavigation() + // const [items, setItems] = useState([]) + + // useEffect(() => { + // setItems(cachedItems) + // }, []) const { chats, @@ -68,9 +84,9 @@ const createStyles = () => container: { ...Platform.select({ android: { - paddingBottom: $size(73) - } - }) + paddingBottom: $size(73), + }, + }), }, searchField: { marginBottom: $size(20, 18), diff --git a/src/modules/chats/smart-components/swipable-chat-row-card.smart-component.tsx b/src/modules/chats/smart-components/swipable-chat-row-card.smart-component.tsx index 6c048501..ef537490 100644 --- a/src/modules/chats/smart-components/swipable-chat-row-card.smart-component.tsx +++ b/src/modules/chats/smart-components/swipable-chat-row-card.smart-component.tsx @@ -2,7 +2,7 @@ import { IChat, SquareButton } from '@/shared' import { useTheme } from '@/shared/hooks/use-theme.hook' import { PartialTheme } from '@/shared/themes/interfaces' import { useFocusEffect } from '@react-navigation/native' -import React, { FC, useRef } from 'react' +import React, { FC, useCallback, useRef } from 'react' import { StyleSheet, View, ViewStyle } from 'react-native' import { Swipeable } from 'react-native-gesture-handler' import { ChatRowCard } from '../components' @@ -28,6 +28,12 @@ interface IProps { style?: ViewStyle } +const prepareBtnsToRender = (config: any) => { + return config.map((it, i) => ( + + )) +} + export const SwipableChatRowCardSmart: FC = ({ id, label, @@ -45,45 +51,46 @@ export const SwipableChatRowCardSmart: FC = ({ const { styles, theme } = useTheme(createStyles) const swipableRef = useRef(null) - const closeSwipeable = () => { + const closeSwipeable = useCallback(() => { swipableRef.current.close() - } + }, [swipableRef.current]) useFocusEffect(() => closeSwipeable()) - const prepareBtnsToRender = (config: any) => { - const btnsToRender = config.map(it => { - return - }) - return btnsToRender - } - - const rightBtnsToRender = prepareBtnsToRender( - chatRowCardButtonsConfig.rightBtn({ - id, - unreadMessagesCount, - theme, - onPress: onPressActionBtn, - afterPress: closeSwipeable, - }), + const rightBtnsToRender = useCallback( + () => + prepareBtnsToRender( + chatRowCardButtonsConfig.rightBtn({ + id, + theme, + onPress: onPressActionBtn, + afterPress: closeSwipeable, + }), + ), + [id, unreadMessagesCount, closeSwipeable], ) - const leftBtnsToRender = prepareBtnsToRender( - chatRowCardButtonsConfig.leftBtn({ - id, - unreadMessagesCount, - theme, - onPress: onPressActionBtn, - afterPress: closeSwipeable, - isChatFixed: isPinned, - isChatUnread: isUnread, - }), + + const leftBtnsToRender = useCallback( + () => + prepareBtnsToRender( + chatRowCardButtonsConfig.leftBtn({ + id, + unreadMessagesCount, + theme, + onPress: onPressActionBtn, + afterPress: closeSwipeable, + isChatFixed: isPinned, + isChatUnread: isUnread, + }), + ), + [id, unreadMessagesCount, theme, isPinned, isUnread, closeSwipeable], ) return ( leftBtnsToRender} - renderRightActions={() => rightBtnsToRender}> + renderLeftActions={leftBtnsToRender} + renderRightActions={rightBtnsToRender}> void +} + +export const ExecutorsSliderListSmart: FC = ({ + searchString, + onLoaded, +}) => { + const nav = useNav() + + const { + items: executors, + isLoading, + setLoadParams, + count, + resetFlatList, + isEmpty, + } = useFlatList({ + fetchItems: fetchTPermittedExecutorsReq, + needInit: false, + serrializatorItems: (_items: IPermittedExecutorResponse[]) => + transformPermittedExecutors(_items), + loadParams: { + sort: 'DESC', + sortField: 'id', + searchString, + }, + limit: 5, + defaultLoading: false, + skeletonDataKey: SkeletonDataKey.HomeExecutors, + clearWhenReload: false, + defaultItems: [], + }) + + useEffect(() => { + InteractionManager.runAfterInteractions(() => { + resetFlatList() + }) + }, []) + + useEffect(() => { + if (onLoaded) onLoaded(count) + }, [count]) + + useEffect(() => { + if (searchString !== null) setLoadParams({ searchString }) + }, [searchString]) + + const renderItem = item => { + return ( + + + nav.navigate(RouteKey.ExecutorTasks, { + userData: item, + }) + } + onPressInfo={() => + nav.navigate(RouteKey.ContactDetail, { + contactId: item.id, + }) + } + /> + + ) + } + const pressExecutorsHandler = useCallback(() => { + nav.navigate(RouteKey.Executors) + }, [nav.navigate]) + + if (searchString !== null && !isLoading && isEmpty) return null + + const renderContent = () => { + if (isLoading) { + return ( + + + + ) + } + + return ( + + {executors.map(renderItem)} + + ) + } + + return ( + + {renderContent()} + + ) +} + +export const styles = StyleSheet.create({ + loading: { + justifyContent: 'center', + alignItems: 'center', + }, + container: { + width: screenWidth - 40, + marginLeft: 20, + }, + cardContainer: { + width: '100%', + }, + content: { + marginTop: $size(15, 10), + }, +}) diff --git a/src/modules/executors/smart-components/index.ts b/src/modules/executors/smart-components/index.ts new file mode 100644 index 00000000..039157d9 --- /dev/null +++ b/src/modules/executors/smart-components/index.ts @@ -0,0 +1 @@ +export * from './executors-slider-list.smart-component' diff --git a/src/modules/groups/hooks/use-fetch-groups-with-tasks-count.hook.ts b/src/modules/groups/hooks/use-fetch-groups-with-tasks-count.hook.ts index f57e09c9..7020febb 100644 --- a/src/modules/groups/hooks/use-fetch-groups-with-tasks-count.hook.ts +++ b/src/modules/groups/hooks/use-fetch-groups-with-tasks-count.hook.ts @@ -1,6 +1,5 @@ import { useEffect, useState } from 'react' import { ITaxonomyWithTasksCount, useFlatList } from '@/shared' -import { taxonomiesService } from '@/services/domain' import { fetchGroupsWithTasksCountReq } from '@/api' export const useFetchGroupsWithTasksCount = ( diff --git a/src/modules/groups/mock/groups-data.mock.ts b/src/modules/groups/mock/groups-data.mock.ts index b026758e..c3959eb6 100644 --- a/src/modules/groups/mock/groups-data.mock.ts +++ b/src/modules/groups/mock/groups-data.mock.ts @@ -1,42 +1,14 @@ -export const groupsData = [ - { - title: 'Title', - info: '25 Pflfx', - }, - { - title: 'Title', - info: 'Info', - }, - { - title: 'Title', - info: 'Info', - }, - { - title: 'Title', - info: 'Info', - }, - { - title: 'Title', - info: 'Info', - }, - { - title: 'Title', - info: 'Info', - }, - { - title: 'Title', - info: 'Info', - }, - { - title: 'Title', - info: 'Info', - }, - { - title: 'Title', - info: 'Info', - }, - { - title: 'Title', - info: 'Info', +import { ITaxonomyWithTasksCount } from '@/shared' + +export const groupsData: ITaxonomyWithTasksCount[] = [ + { + id: null, + parentId: null, + name: null, + type: null, + isDeleted: null, + isDefault: null, + iconUrl: null, + tasksCount: null, }, ] diff --git a/src/modules/groups/mock/index.ts b/src/modules/groups/mock/index.ts new file mode 100644 index 00000000..2cd31e4d --- /dev/null +++ b/src/modules/groups/mock/index.ts @@ -0,0 +1 @@ +export * from './groups-data.mock' diff --git a/src/modules/groups/smart-components/groups-slider-list.smart-component.tsx b/src/modules/groups/smart-components/groups-slider-list.smart-component.tsx new file mode 100644 index 00000000..d0af25cd --- /dev/null +++ b/src/modules/groups/smart-components/groups-slider-list.smart-component.tsx @@ -0,0 +1,148 @@ +import React, { FC, useCallback, useEffect } from 'react' +import { + InfoRowCard, + ITaxonomyWithTasksCount, + RouteKey, + useFlatList, + useNav, +} from '@/shared' +import { $size, getTitleByCount } from '@/shared/helpers' +import { + ActivityIndicator, + Dimensions, + InteractionManager, + StyleSheet, + View, +} from 'react-native' +import Swiper from 'react-native-swiper' +import { fetchGroupsWithTasksCountReq } from '@/api' +import { SkeletonDataKey } from '@/services/system' +import { ContentBlock } from '@/shared/components/blocks' + +const { width: screenWidth } = Dimensions.get('screen') + +interface GroupsSliderListProps { + searchString: string + onLoaded?: (count: number) => void +} + +export const GroupsSliderList: FC = ({ + searchString, + onLoaded, +}) => { + const nav = useNav() + + const { + items: groups, + isLoading, + resetFlatList, + setLoadParams, + count, + isEmpty, + } = useFlatList({ + fetchItems: fetchGroupsWithTasksCountReq, + needInit: false, + loadParams: { + withoutEmpty: true, + sort: 'DESC', + sortField: 'id', + }, + limit: 30, + defaultLoading: false, + skeletonDataKey: SkeletonDataKey.HomeGroups, + clearWhenReload: false, + defaultItems: [], + }) + + useEffect(() => { + InteractionManager.runAfterInteractions(() => { + resetFlatList() + }) + }, []) + + useEffect(() => { + if (onLoaded) onLoaded(count) + }, [count]) + + useEffect(() => { + if (searchString !== null) setLoadParams({ searchString }) + }, [searchString]) + + const renderItem = useCallback(item => { + if (!item || !item.tasksCount) return null + return ( + + + nav.navigate(RouteKey.GroupTasks, { + groupData: item, + }) + } + onPressInfo={() => + console.log('i press group info', item.name) + } + /> + + ) + }, []) + + const pressGroupHandler = useCallback(() => { + nav.navigate(RouteKey.Group) + }, [nav.navigate]) + + if (searchString !== null && !isLoading && isEmpty) return null + + const renderContent = () => { + if (isLoading) { + return ( + + + + ) + } + return ( + + {groups.map(renderItem)} + + ) + } + + return ( + + {renderContent()} + + ) +} + +const styles = StyleSheet.create({ + loading: { + justifyContent: 'center', + alignItems: 'center', + }, + cardContainer: { + width: screenWidth - 40, + marginLeft: 20, + }, + content: { + marginTop: $size(15, 10), + }, +}) diff --git a/src/modules/groups/smart-components/index.ts b/src/modules/groups/smart-components/index.ts new file mode 100644 index 00000000..6a96961c --- /dev/null +++ b/src/modules/groups/smart-components/index.ts @@ -0,0 +1 @@ +export * from './groups-slider-list.smart-component' diff --git a/src/modules/home/atoms/home-executors.component.tsx b/src/modules/home/atoms/home-executors.component.tsx deleted file mode 100644 index f28076c3..00000000 --- a/src/modules/home/atoms/home-executors.component.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { - $size, - InfoRowCard, - IShortUser, - ITaxonomyWithTasksCount, - RouteKey, - useNav, -} from '@/shared' -import React, { FC } from 'react' -import { Dimensions, StyleSheet, View } from 'react-native' -import { HomeCarusel } from '../components' - -interface Props { - items: IShortUser[] -} -const { width: screenWidth } = Dimensions.get('screen') - -export const HomeExecutorsBlock: FC = ({ items }) => { - const { navigate } = useNav() - - const renderItem = item => { - return ( - - - navigate(RouteKey.ExecutorTasks, { userData: item }) - } - onPressInfo={() => - navigate(RouteKey.ContactDetail, { contactId: item.id }) - } - /> - - ) - } - return {items.map(renderItem)} -} - -const styles = StyleSheet.create({ - container: { - width: screenWidth - 40, - marginLeft: 20, - }, - cardContainer: { - width: '100%', - }, -}) diff --git a/src/modules/home/atoms/home-groups-block.componen.tsx b/src/modules/home/atoms/home-groups-block.componen.tsx deleted file mode 100644 index 42da326f..00000000 --- a/src/modules/home/atoms/home-groups-block.componen.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { - InfoRowCard, - ITaxonomyWithTasksCount, - RouteKey, - useNav, -} from '@/shared' -import { $size, getTitleByCount } from '@/shared/helpers' -import React, { FC } from 'react' -import { Dimensions, StyleSheet, View } from 'react-native' -import { HomeCarusel } from '../components' - -interface Props { - items: ITaxonomyWithTasksCount[] -} -const { width: screenWidth } = Dimensions.get('screen') - -export const HomeGroupsBlock: FC = ({ items }) => { - const { navigate } = useNav() - - const renderItem = item => { - if (!item || !item.tasksCount) return null - return ( - - - navigate(RouteKey.GroupTasks, { - groupData: item, - }) - } - onPressInfo={() => - console.log('i press group info', item.name) - } - /> - - ) - } - return {items.map(renderItem)} -} - -const styles = StyleSheet.create({ - cardContainer: { - width: screenWidth - 40, - marginLeft: 20, - }, -}) diff --git a/src/modules/home/atoms/index.ts b/src/modules/home/atoms/index.ts deleted file mode 100644 index e5c77666..00000000 --- a/src/modules/home/atoms/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './home-groups-block.componen'; -export * from './home-executors.component' \ No newline at end of file diff --git a/src/modules/home/helpers/index.ts b/src/modules/home/helpers/index.ts new file mode 100644 index 00000000..52756048 --- /dev/null +++ b/src/modules/home/helpers/index.ts @@ -0,0 +1,24 @@ +import { getTitleByCount } from '@/shared/helpers' +import _ from 'lodash' + +export const getHomeSearchResult = ( + counts: Record, + searchString: string, +) => { + if ( + !searchString || + !_.isNumber(counts.tasks) || + !_.isNumber(counts.groups) || + !_.isNumber(counts.executors) + ) + return false + + const count = + Number(counts.executors) + Number(counts.groups) + Number(counts.tasks) + + return `Знайдено ${getTitleByCount(count, [ + 'результат', + 'результата', + 'результатів', + ])}` +} diff --git a/src/modules/home/screens/home.screen.tsx b/src/modules/home/screens/home.screen.tsx index 673f1f51..c2a0da4c 100644 --- a/src/modules/home/screens/home.screen.tsx +++ b/src/modules/home/screens/home.screen.tsx @@ -1,11 +1,12 @@ -import React, { FC, useEffect, useMemo, useState } from 'react' -import { - ActivityIndicator, - ScrollView, - StyleSheet, - TouchableOpacity, - View, -} from 'react-native' +import React, { + FC, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react' +import { ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native' import { $size, IconComponent, @@ -16,28 +17,30 @@ import { useResetOnBlur, } from '@/shared' import { SearchForm } from '@/shared/components/forms' -import { ContentBlock } from '@/shared/components/blocks' import { PartialTheme } from '@/shared/themes/interfaces' import { useTheme } from '@/shared/hooks/use-theme.hook' import { MyTasksListSmart } from '../smart-components' -import { useFetchPermittedExecutors } from '@/modules/executors/hooks' -import { useFetchGroupsWithTasksCount } from '@/modules/groups/hooks' import { useTaskFilter } from '@/modules/tasks/hooks' -import { HomeExecutorsBlock, HomeGroupsBlock } from '../atoms' import { needRedirect } from '@/services/system/notification.service' -import { getTitleByCount } from '@/shared/helpers' -import { fetchTPermittedExecutorsReq } from '@/api' +import { GroupsSliderList } from '@/modules/groups/smart-components' +import { ExecutorsSliderListSmart } from '@/modules/executors/smart-components' +import _ from 'lodash' +import { getHomeSearchResult } from '../helpers' interface IProps extends IRouteParams {} +const defaultCountsState = { + tasks: null, + groups: null, + executors: null, +} + export const HomeScreen: FC = ({ navigation }) => { const { styles, theme } = useTheme(createStyles) const [searchValue, setValue] = useState(null) const [isOpenDrawer, setDrawer] = useState(false) - const [tasksCount, setTasksCount] = useState(0) - const [alreadyToLoadTasks, setAlreadyToLoad] = useState(false) - + const countsRef = useRef(defaultCountsState) const taskFilter = useTaskFilter({ onSubmit: () => { setDrawer(false) @@ -50,98 +53,55 @@ export const HomeScreen: FC = ({ navigation }) => { useResetOnBlur(taskFilter.onClearFilter) - const respExecutors = useFetchPermittedExecutors( - { - sort: 'DESC', - sortField: 'id', - searchString: searchValue, - }, - 5, - ) - const respGroups = useFetchGroupsWithTasksCount( - { - withoutEmpty: true, - sort: 'DESC', - sortField: 'id', - searchString: searchValue, - }, - 5, - ) - useEffect(() => { - if (respExecutors.isLoading && !alreadyToLoadTasks) { - setTimeout(() => { - setAlreadyToLoad(true) - }, 100) - } - }, [respExecutors.count]) - - useEffect(() => { - if (searchValue !== null) { - respExecutors.setLoadParams({ searchString: searchValue }) - respGroups.setLoadParams({ searchString: searchValue }) - } - }, [searchValue]) - - useEffect(() => { - setTimeout(() => { + const timmer = setTimeout(() => { if (needRedirect.to) { navigation.navigate(needRedirect.to, needRedirect.payload) needRedirect.to = null needRedirect.payload = null } }, 500) + return () => clearTimeout(timmer) }, []) - const groupsMemoRender = useMemo(() => { - if (respGroups.isLoading) { - return ( - - - - ) - } - return - }, [respGroups]) - - const executorsMemoRender = useMemo(() => { - if (respExecutors.isLoading) { - return ( - - - - ) - } - return ( - - ) - }, [respExecutors]) - - const showResult = useMemo( - () => - searchValue && !respExecutors?.isLoading && !respGroups?.isLoading, - [searchValue, respExecutors.isLoading, respGroups.isLoading], + useEffect(() => { + if (!searchValue) countsRef.current = defaultCountsState + }, [searchValue]) + + const searchResult = useMemo( + () => getHomeSearchResult(countsRef.current, searchValue), + [ + searchValue, + countsRef.current.tasks, + countsRef.current.executors, + countsRef.current.groups, + ], ) - const resultCount = useMemo( - () => respExecutors?.count + respGroups?.count + tasksCount, - [showResult, respExecutors.count, respGroups.count, tasksCount], + const setTasksCount = useCallback( + count => { + countsRef.current.tasks = count + }, + [countsRef.current], ) - const resultString = useMemo( - () => - `Знайдено ${getTitleByCount(resultCount, [ - 'результат', - 'результата', - 'результатів', - ])}`, - [resultCount], + const setExecutorsCount = useCallback( + count => { + countsRef.current.executors = count + }, + [countsRef.current], ) - const onTasksLoaded = (count: number) => setTasksCount(count) + const setGroupsCount = useCallback( + count => { + countsRef.current.groups = count + }, + [countsRef.current], + ) + + const openDrawer = useCallback(() => { + setDrawer(true) + }, [setDrawer]) return ( = ({ navigation }) => { searchValue={searchValue} placeholder="Знайдіть задачу, групу" onChange={setValue} - onFocus={() => {}} paddingHorizontal={20} leftComponent={() => ( - setDrawer(true)}> + = ({ navigation }) => { leftContainerStyle={styles.searchContainer} /> - {showResult ? ( + {searchResult ? ( - {resultString} + {searchResult} ) : null} - navigation.navigate(RouteKey.Group)} - paddingHorizontal={$size(0)} - style={styles.content}> - {groupsMemoRender} - - - - navigation.navigate(RouteKey.Executors) - } - paddingHorizontal={$size(0)} - style={styles.content}> - {executorsMemoRender} - - - - - + + + + diff --git a/src/modules/home/smart-components/my-tasks-list.smart-component.tsx b/src/modules/home/smart-components/my-tasks-list.smart-component.tsx index df50bdeb..98e1f439 100644 --- a/src/modules/home/smart-components/my-tasks-list.smart-component.tsx +++ b/src/modules/home/smart-components/my-tasks-list.smart-component.tsx @@ -1,5 +1,12 @@ -import React, { useEffect, FC } from 'react' -import { ActivityIndicator, StyleSheet, Text, View } from 'react-native' +import React, { useEffect, FC, useCallback } from 'react' +import { + ActivityIndicator, + InteractionManager, + StyleSheet, + Text, + View, + ViewStyle, +} from 'react-native' import { TasksListHeadSmart, TasksListSmart, @@ -9,7 +16,9 @@ import { $size, ITaskPreview, RouteKey, + TaskStatus, useEventsListener, + useFlatList, useNav, } from '@/shared' import { PartialTheme } from '@/shared/themes/interfaces' @@ -19,19 +28,20 @@ import * as _ from 'lodash' import { useFetchTasksByFilter, useTaskActions, + useTaskListEvents, useTaskSelector, } from '@/modules/tasks/hooks' +import { tasksService } from '@/services/domain' +import { transformTasksInList } from '@/api/tasks/transform' +import { SkeletonDataKey } from '@/services/system' interface IProps { searchString?: string onLoaded?: (count: number) => void - alreadyToLoad?: boolean + contentStyle?: ViewStyle } -export const MyTasksListSmart: FC = ({ - alreadyToLoad = true, - ...props -}) => { +export const MyTasksListSmart: FC = ({ contentStyle, ...props }) => { const nav = useNav() const { styles } = useTheme(createStyles) const { handlePressOneTaskAction } = useTaskActions() @@ -39,17 +49,42 @@ export const MyTasksListSmart: FC = ({ const { selectedTasks, handleSelectTask, handleResetSelected } = useTaskSelector() - const { - items: myTasks, - isLoading, - setLoadParams, - resetTaskList, - count, - } = useFetchTasksByFilter({ - limit: 5, - alreadyToLoad, + const { items, isLoading, setLoadParams, resetFlatList, _setItems, count } = + useFlatList({ + fetchItems: tasksService.getTasks, + serrializatorItems: transformTasksInList, + needInit: false, + loadParams: { + sort: 'DESC', + sortField: 'id', + }, + limit: 5, + defaultLoading: false, + skeletonDataKey: SkeletonDataKey.HomeTasks, + clearWhenReload: false, + defaultItems: [], + }) + + useEffect(() => { + const {} = InteractionManager.runAfterInteractions(() => { + resetFlatList() + }) + }, []) + + const onAction = (key: string) => { + if (key === 'delete') resetFlatList() + } + + useTaskListEvents({ + items, + onReload: () => resetFlatList(), + setItems: (items: ITaskPreview[]) => _setItems(items), + onAction, + taskFilterStatus: TaskStatus.Active, }) + useEventsListener('reloadTask', resetFlatList) + useEffect(() => { if (props.onLoaded) props.onLoaded(count) }, [count]) @@ -59,20 +94,21 @@ export const MyTasksListSmart: FC = ({ setLoadParams({ searchString: props.searchString }) }, [props.searchString]) - const onPressTask = (task: ITaskPreview) => { - nav.navigate(RouteKey.TaskDetails, { - taskId: task.id, - }) - } - - useEventsListener('reloadTask', resetTaskList) + const onPressTask = useCallback( + (task: ITaskPreview) => { + nav.navigate(RouteKey.TaskDetails, { + taskId: task.id, + }) + }, + [nav.navigate], + ) const renderList = ( items: ITaskPreview[], renderItem: TRenderTaskPreviewItem, ) => { return ( - + {_.isEmpty(selectedTasks) ? ( = ({ if (isLoading) return - if (_.isEmpty(myTasks)) return Немає задач + if (_.isEmpty(items)) return Немає задач return ( handleSelectTask(task)} diff --git a/src/modules/root/navigation-groups/tab-bar.group.tsx b/src/modules/root/navigation-groups/tab-bar.group.tsx index cad74813..d3d45dda 100644 --- a/src/modules/root/navigation-groups/tab-bar.group.tsx +++ b/src/modules/root/navigation-groups/tab-bar.group.tsx @@ -23,13 +23,17 @@ export const TabNavigator: FC = () => { return ( ( )}> - + tasksService.getTasks(params), serrializatorItems: (items: ITaskPreviewResponse[]) => transformTasksInList(items) as any, - needInit: false, + needInit: true, loadParams: { sort: 'DESC', sortField: 'id', @@ -76,10 +76,6 @@ export const useFetchTasksByFilter = ({ clearWhenReload: false, }) - useEffect(() => { - if (alreadyToLoad) resetFlatList() - }, [alreadyToLoad]) - useEffect(() => { if (searchString) setLoadParams({ ...getFetchParams(), searchString }) }, [searchString]) @@ -88,15 +84,11 @@ export const useFetchTasksByFilter = ({ setLoadParams({ ...getFetchParams(), searchString }) } - const onAction = (key: string) => { - if (key === 'delete' && limit === 5) resetFlatList() - } - useTaskListEvents({ items, onReload: () => resetFlatList(), setItems: (items: ITaskPreview[]) => _setItems(items), - onAction, + onAction: noop, taskFilterStatus: filter?.status, }) diff --git a/src/services/domain/account.service.ts b/src/services/domain/account.service.ts index 9920b811..22a952f8 100644 --- a/src/services/domain/account.service.ts +++ b/src/services/domain/account.service.ts @@ -2,41 +2,26 @@ import { simpleDispatch } from '@/store/store-helpers' import { SetLoadingAccount, SaveAccount } from '@/store/account' import { fetchAccount } from '@/api' import { IUser } from '@/shared/interfaces' -import AsyncStorage from '@react-native-async-storage/async-storage' +import { Service } from '@/shared/abstract' -const loadAccount = async (version?: string) => { - simpleDispatch(new SetLoadingAccount({ isLoading: true })) - try { - let account: IUser +class AccountService extends Service { + protected prefix = 'account' - if (version) { - const cached = await AsyncStorage.getItem(`acc${version}`) - if (cached) account = JSON.parse(cached) - } + public async loadAccount(version?: string) { + simpleDispatch(new SetLoadingAccount({ isLoading: true })) - if (!account) { - const { data } = await fetchAccount() - account = data - if (version) - await AsyncStorage.setItem( - `acc${version}`, - JSON.stringify(data), - ) + try { + const account = await this.fetchData(fetchAccount) + simpleDispatch(new SaveAccount({ account })) + } catch (e) { + } finally { + simpleDispatch(new SetLoadingAccount({ isLoading: false })) } + } + public setAccount(account: IUser) { simpleDispatch(new SaveAccount({ account })) - } catch (e: any) { - console.log(e) - } finally { - simpleDispatch(new SetLoadingAccount({ isLoading: false })) } } -const setAccount = async (account: IUser) => { - simpleDispatch(new SaveAccount({ account })) -} - -export const accountService = { - loadAccount, - setAccount, -} +export const accountService = new AccountService() diff --git a/src/services/domain/permissions.service.ts b/src/services/domain/permissions.service.ts index 64eb0743..3140351d 100644 --- a/src/services/domain/permissions.service.ts +++ b/src/services/domain/permissions.service.ts @@ -13,6 +13,8 @@ import AsyncStorage from '@react-native-async-storage/async-storage' import _ from 'lodash' class PermissionsService extends Service { + protected prefix = 'prefix' + private getPermissionsForUsers(): IPermissionsState['permissionsForUsers'] { return this.select(selectPermissionsForUsers) } diff --git a/src/services/system/index.ts b/src/services/system/index.ts index 7f99642c..df083fe4 100644 --- a/src/services/system/index.ts +++ b/src/services/system/index.ts @@ -5,4 +5,5 @@ export * from './storage.service' export * from './device-info.service' export * from './real-time.service' export * from './media.service' -export * from './fs.service' \ No newline at end of file +export * from './fs.service' +export * from './skeleton-data.service' diff --git a/src/services/system/skeleton-data.service.ts b/src/services/system/skeleton-data.service.ts new file mode 100644 index 00000000..99d96575 --- /dev/null +++ b/src/services/system/skeleton-data.service.ts @@ -0,0 +1,47 @@ +import AsyncStorage from '@react-native-async-storage/async-storage' +import _ from 'lodash' + +export enum SkeletonDataKey { + HomeGroups = 'homeGroups', + HomeExecutors = 'homeExecutors', + HomeTasks = 'homeTasks', +} + +export class SkeletonData { + private dataKeys = [ + SkeletonDataKey.HomeGroups, + SkeletonDataKey.HomeExecutors, + SkeletonDataKey.HomeTasks, + ] + + private loadedData: Partial> = {} + + constructor() { + this.init() + } + + private async init() { + for await (const key of this.dataKeys) { + await this.load(key) + } + console.log('LOAD FINISH') + } + + private async load(key: SkeletonDataKey) { + const data = await AsyncStorage.getItem(key) + if (!data) return + this.loadedData[key] = JSON.parse(data) + console.log(key, this.loadedData) + } + + public set(key: SkeletonDataKey, data: any) { + console.log('set key', key) + AsyncStorage.setItem(key, JSON.stringify(data)) + } + + public get(key: SkeletonDataKey, defaultValue?: any) { + return _.defaultTo(this.loadedData[key], defaultValue) + } +} + +export const skeletonDataService = new SkeletonData() diff --git a/src/shared/abstract/service.ts b/src/shared/abstract/service.ts index d3d942a5..968c97db 100644 --- a/src/shared/abstract/service.ts +++ b/src/shared/abstract/service.ts @@ -1,7 +1,11 @@ import store, { RootState } from '@/store' import { selectId } from '@/store/account' +import AsyncStorage from '@react-native-async-storage/async-storage' +import { AxiosResponse } from 'axios' + +export abstract class Service { + protected abstract prefix: string -export class Service { protected select(selector: (store: RootState) => any) { return selector(store.getState()) } @@ -9,4 +13,30 @@ export class Service { protected selectUserId() { return this.select(selectId) } + + protected async fetchData( + req: (...args: any[]) => Promise>, + version?: string, + ): Promise { + let result: T + + if (version) { + const cached = await AsyncStorage.getItem( + `${this.prefix}${version}`, + ) + if (cached) result = JSON.parse(cached) + } + + if (!result) { + const { data } = await req() + result = data + if (version) + await AsyncStorage.setItem( + `${this.prefix}${version}`, + JSON.stringify(data), + ) + } + + return result + } } diff --git a/src/shared/components/forms/form-search.component.tsx b/src/shared/components/forms/form-search.component.tsx index 5bde0b87..beec432b 100644 --- a/src/shared/components/forms/form-search.component.tsx +++ b/src/shared/components/forms/form-search.component.tsx @@ -10,6 +10,7 @@ import { IconComponent } from '../elements' import { $size } from '@/shared/helpers' import { PartialTheme } from '@/shared/themes/interfaces' import { useTheme } from '@/shared/hooks/use-theme.hook' +import { noop } from 'lodash' interface IProps { searchValue: string @@ -35,8 +36,8 @@ export const SearchForm: FC = ({ containerStyle, borderBottom = true, onChange, - onFocus = () => {}, - onBlur = () => {}, + onFocus = noop, + onBlur = noop, leftComponent, leftContainerStyle, rightComponent, diff --git a/src/shared/components/headers/primary-header.component.tsx b/src/shared/components/headers/primary-header.component.tsx index 3c180a00..e783db69 100644 --- a/src/shared/components/headers/primary-header.component.tsx +++ b/src/shared/components/headers/primary-header.component.tsx @@ -11,6 +11,7 @@ interface IProps { bottomBorder?: boolean goBack?: () => void rightComponents?: () => ReactElement | ReactElement[] + rightComponent?: ReactElement } export const PrimaryHeader: FC = ({ @@ -19,6 +20,7 @@ export const PrimaryHeader: FC = ({ bottomBorder = true, goBack, rightComponents, + rightComponent = null, }) => { const { styles, theme } = useTheme(createStyles) @@ -45,6 +47,7 @@ export const PrimaryHeader: FC = ({ {rightComponents && rightComponents()} + {rightComponent} ) diff --git a/src/shared/components/layouts/screen-layout.component.tsx b/src/shared/components/layouts/screen-layout.component.tsx index 890d32bb..0378a7c1 100644 --- a/src/shared/components/layouts/screen-layout.component.tsx +++ b/src/shared/components/layouts/screen-layout.component.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement } from 'react' +import React, { ReactElement, useCallback } from 'react' import { StatusBar, StyleSheet, View, ViewStyle } from 'react-native' import { ScreenLayoutContent } from './screen-layout-content.component' import { PrimaryHeader } from '../headers' @@ -42,6 +42,13 @@ interface ScreenLayoutProps { export const ScreenLayout = (props: ScreenLayoutProps) => { const { styles, themeTitle } = useTheme(createStyles) + + const header = useCallback(() => { + if (props.header) return + if (props.headerComponent) return props.headerComponent + return null + }, [props.header, props.headerComponent]) + const layout = ( { } /> - { - if (props.header) return - if (props.headerComponent) return props.headerComponent - }} - /> + {props.footer ? props.footer() : null} diff --git a/src/shared/enums/storage-keys.enum.ts b/src/shared/enums/storage-keys.enum.ts index a3320051..eb32270c 100644 --- a/src/shared/enums/storage-keys.enum.ts +++ b/src/shared/enums/storage-keys.enum.ts @@ -3,5 +3,6 @@ export enum StorageKey { RefreshToken = 'RefreshToken', Theme = 'Theme', ChatBgId = 'ChatBgId', - CustomChatBg = 'CustomChatBg' + CustomChatBg = 'CustomChatBg', + Chats = 'chats', } diff --git a/src/shared/hooks/use-flat-list.hook.ts b/src/shared/hooks/use-flat-list.hook.ts index 9d2c5298..8c0f42af 100644 --- a/src/shared/hooks/use-flat-list.hook.ts +++ b/src/shared/hooks/use-flat-list.hook.ts @@ -1,3 +1,5 @@ +import { SkeletonDataKey, skeletonDataService } from '@/services/system' +import _ from 'lodash' import { useState, useRef, useEffect, useMemo } from 'react' interface IProps { @@ -9,6 +11,9 @@ interface IProps { loadParams?: { [key: string]: string | boolean } needInit: boolean clearWhenReload?: boolean + defaultItems?: T[] + defaultLoading?: boolean + skeletonDataKey?: SkeletonDataKey } const defaultProps: IProps = { @@ -20,6 +25,9 @@ const defaultProps: IProps = { loadParams: {}, needInit: true, clearWhenReload: true, + defaultItems: null, + defaultLoading: true, + skeletonDataKey: null, } const getDefaultProps = () => { @@ -41,17 +49,25 @@ export const useFlatList = (props: IProps) => { const blockLoadingRef = useRef(false) - const [items, setItems] = useState(null) + const [items, setItems] = useState(props.defaultItems) const [totalCount, setTotalCount] = useState(0) - const [isLoading, setLoading] = useState(true) + const [isLoading, setLoading] = useState(props.defaultLoading) const [isLoadingNext, setIsLoadingNext] = useState(false) const [isInit, setIsInit] = useState(false) + useEffect(() => { + if (props.skeletonDataKey) { + const items = skeletonDataService.get(props.skeletonDataKey) + if (items) setItems(_.defaultTo(items, [])) + else setLoading(true) + } + }, []) + const fetchItems = async (firstFetch = false) => { const { count, page, limit } = loadParams.current if (firstFetch) { - setLoading(true) + if (isInit || !props.skeletonDataKey) setLoading(true) loadParams.current.page = defaultProps.page loadParams.current.count = undefined } else if (blockLoadingRef.current) return @@ -82,7 +98,11 @@ export const useFlatList = (props: IProps) => { setTotalCount(response.data.count) - if (!isInit) setIsInit(true) + if (!isInit) { + if (props.skeletonDataKey) + skeletonDataService.set(props.skeletonDataKey, fetchedItems) + setIsInit(true) + } } catch (e) { console.log(e) setItems([]) diff --git a/yarn.lock b/yarn.lock index ca245cfc..cf123ccc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1540,7 +1540,7 @@ "resolved" "https://registry.npmjs.org/@react-native-community/clipboard/-/clipboard-1.5.1.tgz" "version" "1.5.1" -"@react-native-community/datetimepicker@>=6.1.3": +"@react-native-community/datetimepicker@^6.1.3", "@react-native-community/datetimepicker@>=6.1.3": "integrity" "sha512-3e20BPQy8QiQeIGJL4zoJaRsGWB31hl9i7oTZECWxnK6nVUeqGt/G+JRJlJinmqL6z4WePHC6RSUMgpC64OOwg==" "resolved" "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-6.1.3.tgz" "version" "6.1.3" @@ -3480,7 +3480,7 @@ "resolved" "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" "version" "2.0.0" -"deprecated-react-native-prop-types@>=2.3.0": +"deprecated-react-native-prop-types@^2.3.0", "deprecated-react-native-prop-types@>=2.3.0": "integrity" "sha512-pWD0voFtNYxrVqvBMYf5gq3NA2GCpfodS1yNynTPc93AYA/KEMGeWDqqeUB6R2Z9ZofVhks2aeJXiuQqKNpesA==" "resolved" "https://registry.npmjs.org/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-2.3.0.tgz" "version" "2.3.0"