@messenger-box/platform-mobile 0.0.1-alpha.338 → 0.0.1-alpha.340

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,103 @@
1
+ import React, { useEffect, useState, useRef } from 'react';
2
+ import { Image } from 'native-base';
3
+ // import { Image } from "react-native"
4
+ import * as FileSystem from 'expo-file-system';
5
+
6
+ import * as CONST from './consts';
7
+
8
+ const CachedImage = (props: any) => {
9
+ const { source, cacheKey, placeholderContent } = props;
10
+ const { uri, headers, expiresIn } = source;
11
+ const fileURI = `${CONST.IMAGE_CACHE_FOLDER}${cacheKey}`;
12
+
13
+ const [imgUri, setImgUri] = useState<any>(fileURI);
14
+
15
+ const componentIsMounted = useRef(true);
16
+ const requestOption = headers ? { headers } : {};
17
+
18
+ const _callback = (downloadProgress: any) => {
19
+ if (componentIsMounted.current === false) {
20
+ downloadResumableRef.current.pauseAsync();
21
+ FileSystem.deleteAsync(fileURI, { idempotent: true }); // delete file locally if it was not downloaded properly
22
+ }
23
+ };
24
+
25
+ const downloadResumableRef = useRef(FileSystem.createDownloadResumable(uri, fileURI, requestOption, _callback));
26
+
27
+ useEffect(() => {
28
+ loadImage();
29
+ return () => {
30
+ componentIsMounted.current = false;
31
+ };
32
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
33
+
34
+ const loadImage = async () => {
35
+ try {
36
+ // Use the cached image if it exists
37
+ const metadata: any = await FileSystem.getInfoAsync(fileURI);
38
+ const expired = expiresIn && new Date().getTime() / 1000 - metadata?.modificationTime > expiresIn;
39
+ // console.log({expiresIn, expired})
40
+
41
+ // console.log({modificationTime: metadata.modificationTime, currentTime: new Date().getTime() / 1000})
42
+ // console.log({metadata})
43
+ if (!metadata.exists || metadata?.size === 0 || expired) {
44
+ if (componentIsMounted.current) {
45
+ setImgUri(null);
46
+
47
+ if (expired) {
48
+ await FileSystem.deleteAsync(fileURI, { idempotent: true });
49
+ }
50
+ // download to cache
51
+ setImgUri(null);
52
+
53
+ const response: any = await downloadResumableRef.current.downloadAsync();
54
+ if (componentIsMounted.current && response.status === 200) {
55
+ setImgUri(`${fileURI}?`); // deep clone to force re-render
56
+ }
57
+ if (response.status !== 200) {
58
+ FileSystem.deleteAsync(fileURI, { idempotent: true }); // delete file locally if it was not downloaded properly
59
+ }
60
+ }
61
+ }
62
+ } catch (err) {
63
+ // console.log({ err })
64
+ }
65
+ };
66
+ // console.log({placeholderContent})
67
+ if (!imgUri) return placeholderContent || null;
68
+
69
+ return (
70
+ <Image
71
+ // eslint-disable-next-line react/jsx-props-no-spreading
72
+ {...props}
73
+ source={{
74
+ ...source,
75
+ uri: imgUri,
76
+ }}
77
+ />
78
+ );
79
+ };
80
+
81
+ export const CacheManager = {
82
+ addToCache: async ({ file, key }: any) => {
83
+ await FileSystem.copyAsync({
84
+ from: file,
85
+ to: `${CONST.IMAGE_CACHE_FOLDER}${key}`,
86
+ });
87
+ // const uri = await FileSystem.getContentUriAsync(`${CONST.IMAGE_CACHE_FOLDER}${key}`)
88
+ // return uri
89
+ const uri = await CacheManager.getCachedUri({ key });
90
+ return uri;
91
+ },
92
+
93
+ getCachedUri: async ({ key }: any) => {
94
+ const uri = await FileSystem.getContentUriAsync(`${CONST.IMAGE_CACHE_FOLDER}${key}`);
95
+ return uri;
96
+ },
97
+
98
+ downloadAsync: async ({ uri, key, options }: any) => {
99
+ return await FileSystem.downloadAsync(uri, `${CONST.IMAGE_CACHE_FOLDER}${key}`, options);
100
+ },
101
+ };
102
+
103
+ export default CachedImage;
@@ -1,10 +1,12 @@
1
1
  /* eslint-disable no-underscore-dangle, no-use-before-define */
2
2
  import React from 'react';
3
- import { Text, StyleSheet, TouchableOpacity, View, Platform } from 'react-native';
3
+ import { Text, StyleSheet, TouchableOpacity, View, Platform, Dimensions } from 'react-native';
4
4
 
5
5
  import { MessageText, MessageImage, Time, utils } from 'react-native-gifted-chat';
6
-
6
+ import CachedImage from '../CachedImage';
7
7
  const { isSameUser, isSameDay } = utils;
8
+ const windowWidth = Dimensions.get('window').width;
9
+ const windowHeight = Dimensions.get('window').height;
8
10
 
9
11
  export default class Bubble extends React.Component<any> {
10
12
  static defaultProps: {
@@ -22,8 +24,8 @@ export default class Bubble extends React.Component<any> {
22
24
  tickStyle: {};
23
25
  containerToNextStyle: {};
24
26
  containerToPreviousStyle: {};
25
- isShowImageViewer:false,
26
- setImageViewer:(obj:any,v:boolean) => void,
27
+ isShowImageViewer: false;
28
+ setImageViewer: (obj: any, v: boolean) => void;
27
29
  };
28
30
  static propTypes: any;
29
31
  constructor(props: any) {
@@ -59,8 +61,33 @@ export default class Bubble extends React.Component<any> {
59
61
  if (this.props.renderMessageImage) {
60
62
  return this.props.renderMessageImage(messageImageProps);
61
63
  }
64
+ const { image, _id } = messageImageProps?.currentMessage;
65
+
62
66
  return (
63
- <TouchableOpacity onPress={()=>this.props.setImageViewer(messageImageProps?.currentMessage,true)}><Text><MessageImage {...messageImageProps} imageStyle={[styles.slackImage, messageImageProps.imageStyle]} /></Text></TouchableOpacity>
67
+ <TouchableOpacity onPress={() => this.props.setImageViewer(messageImageProps?.currentMessage, true)}>
68
+ <View
69
+ style={{
70
+ width: windowWidth - (windowWidth - 150),
71
+ height: windowHeight - (windowHeight - 100),
72
+ }}
73
+ >
74
+ {/* <MessageImage
75
+ {...messageImageProps}
76
+ imageStyle={[styles.slackImage, messageImageProps.imageStyle]}
77
+ /> */}
78
+ <CachedImage
79
+ style={[styles.slackImage, { width: '100%', height: '100%' }]}
80
+ cacheKey={`${_id}-slack-bubble-imageKey`}
81
+ source={{
82
+ uri: image,
83
+ //headers: `Authorization: Bearer ${token}`,
84
+ expiresIn: 86400,
85
+ }}
86
+ resizeMode={'cover'}
87
+ alt={'image'}
88
+ />
89
+ </View>
90
+ </TouchableOpacity>
64
91
  );
65
92
  }
66
93
  return null;
@@ -243,6 +270,6 @@ Bubble.defaultProps = {
243
270
  tickStyle: {},
244
271
  containerToNextStyle: {},
245
272
  containerToPreviousStyle: {},
246
- isShowImageViewer:false,
247
- setImageViewer:(obj:any,v:boolean) => null,
273
+ isShowImageViewer: false,
274
+ setImageViewer: (obj: any, v: boolean) => null,
248
275
  };
@@ -1,6 +1,6 @@
1
1
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
2
  import { Box, Button, HStack, Icon, Image, Spinner, Text, useColorModeValue, ScrollView } from 'native-base';
3
- import { Platform } from 'react-native';
3
+ import { Platform, TouchableOpacity } from 'react-native';
4
4
  import { useNavigation, useFocusEffect } from '@react-navigation/native';
5
5
  import { useSelector } from 'react-redux';
6
6
  import { orderBy, uniqBy } from 'lodash';
@@ -20,7 +20,7 @@ import { IFileInfo } from '@messenger-box/core';
20
20
  import { config } from '../config';
21
21
  import { userSelector } from '@adminide-stack/user-auth0-client';
22
22
  import { SlackMessage, ImageViewerModal } from '../components/SlackMessageContainer';
23
-
23
+ import CachedImage from '../components/CachedImage';
24
24
  const {
25
25
  MESSAGES_PER_PAGE,
26
26
  CALL_TO_ACTION_BOX_BGCOLOR,
@@ -138,7 +138,8 @@ const ConversationViewComponent = ({ channelId }: any) => {
138
138
  ?.map((et: any) => et?.token) ?? [],
139
139
  )
140
140
  ?.flat(1)
141
- ?.filter((t: any) => t) ?? [];
141
+ ?.filter((t: any) => t)
142
+ ?.filter((value: any, index: any, array: any) => array.indexOf(value) === index) ?? [];
142
143
 
143
144
  if (tokens?.length > 0) setExpoTokens(tokens);
144
145
  }
@@ -603,8 +604,21 @@ const ConversationViewComponent = ({ channelId }: any) => {
603
604
 
604
605
  const modalContent = React.useMemo(() => {
605
606
  if (!imageObject) return <></>;
606
- const { image } = imageObject;
607
- return <Image resizeMode={'cover'} alt={'image'} source={{ uri: image }} size={'full'} />;
607
+ const { image, _id } = imageObject;
608
+ return (
609
+ <CachedImage
610
+ style={{ width: '100%', height: '100%' }}
611
+ resizeMode={'cover'}
612
+ // cacheKey={`${_id}-conversation-modal-image-key`}
613
+ cacheKey={`${_id}-slack-bubble-imageKey`}
614
+ source={{
615
+ uri: image,
616
+ //headers: `Authorization: Bearer ${token}`,
617
+ expiresIn: 86400,
618
+ }}
619
+ alt={'image'}
620
+ />
621
+ );
608
622
  }, [imageObject]);
609
623
 
610
624
  const renderMessage = (props: any) => {
@@ -1,5 +1,5 @@
1
1
  import React, { useCallback, useMemo, useEffect, useState } from 'react';
2
- import { FlatList, Box, Heading, Input, Text, Icon, Center } from 'native-base';
2
+ import { FlatList, Box, Heading, Input, Text, Icon, Center, Spinner } from 'native-base';
3
3
  import { Ionicons } from '@expo/vector-icons';
4
4
  import { useSelector, useDispatch } from 'react-redux';
5
5
  import { useNavigation, useRoute, useIsFocused, useFocusEffect } from '@react-navigation/native';
@@ -15,14 +15,15 @@ export interface InboxProps {
15
15
  channelRole?: string;
16
16
  }
17
17
 
18
- const DialogsComponent = (props: InboxProps) => {
18
+ const DialogsComponent = (props: InboxProps) => {
19
19
  const { channelFilters, channelRole } = props;
20
20
  const { params } = useRoute<any>();
21
21
  const auth = useSelector(userSelector);
22
22
  const dispatch = useDispatch();
23
23
  const navigation = useNavigation<any>();
24
24
  const isFocused = useIsFocused();
25
- const [userDirectChannel, setUserDirectChannel] = useState<any>([]);
25
+ const [refreshing, setRefresh] = useState<boolean>(false);
26
+ // const [userDirectChannel, setUserDirectChannel] = useState<any>([]);
26
27
 
27
28
  const {
28
29
  data: userChannels,
@@ -38,6 +39,7 @@ export interface InboxProps {
38
39
  useFocusEffect(
39
40
  React.useCallback(() => {
40
41
  // Do something when the screen is focused
42
+ setRefresh(false);
41
43
  getChannelsRefetch({ role: channelRole, criteria: channelFilters });
42
44
 
43
45
  return () => {
@@ -47,38 +49,55 @@ export interface InboxProps {
47
49
  }, [channelFilters]),
48
50
  );
49
51
 
50
- useEffect(() => {
51
- setTimeout(() => {
52
- dispatch({
53
- type: CHANGE_SETTINGS_ACTION,
54
- payload: {
55
- footerRender: false,
56
- },
57
- } as any);
58
- }, 0);
59
- return () => {
60
- dispatch({
61
- type: CHANGE_SETTINGS_ACTION,
62
- payload: {
63
- footerRender: true,
64
- },
65
- } as any);
66
- };
67
- }, []);
52
+ React.useEffect(() => {
53
+ if (refreshing) getChannelsRefetch({ role: channelRole, criteria: channelFilters });
54
+ }, [refreshing]);
68
55
 
69
- useEffect(() => {
70
- if (userChannels?.channelsByUser) {
71
- //Direct channel
72
- let userDirectChannels: any =
73
- userChannels?.channelsByUser
74
- ?.filter((i: any) => i.type == 'DIRECT')
75
- ?.filter((c: any) =>
76
- c.members.some((u: any) => u?.user?.id != auth?.id && u.user.__typename == 'UserAccount'),
77
- ) ?? [];
56
+ const channels = React.useMemo(() => {
57
+ if (!userChannels?.channelsByUser?.length) return null;
58
+ setRefresh(false);
59
+ let uChannels: any =
60
+ userChannels?.channelsByUser?.filter((c: any) =>
61
+ c.members.some((u: any) => u?.user?.id != auth?.id && u.user.__typename == 'UserAccount'),
62
+ ) ?? [];
63
+ return uChannels || [];
64
+ }, [userChannels]);
78
65
 
79
- if (userDirectChannels?.length > 0) setUserDirectChannel(userDirectChannels);
80
- }
81
- }, [userChannels?.channelsByUser]);
66
+ // useEffect(() => {
67
+ // setTimeout(() => {
68
+ // dispatch({
69
+ // type: CHANGE_SETTINGS_ACTION,
70
+ // payload: {
71
+ // footerRender: false,
72
+ // },
73
+ // } as any);
74
+ // }, 0);
75
+ // return () => {
76
+ // dispatch({
77
+ // type: CHANGE_SETTINGS_ACTION,
78
+ // payload: {
79
+ // footerRender: true,
80
+ // },
81
+ // } as any);
82
+ // };
83
+ // }, []);
84
+
85
+ // useEffect(() => {
86
+ // if (userChannels?.channelsByUser) {
87
+ // if (userChannels?.channelsByUser?.length == 0) {
88
+ // setUserDirectChannel([]);
89
+ // }
90
+ // //Direct channel
91
+ // let userDirectChannels: any =
92
+ // userChannels?.channelsByUser
93
+ // ?.filter((i: any) => i.type == 'DIRECT')
94
+ // ?.filter((c: any) =>
95
+ // c.members.some((u: any) => u?.user?.id != auth?.id && u.user.__typename == 'UserAccount'),
96
+ // ) ?? [];
97
+
98
+ // if (userDirectChannels?.length > 0) setUserDirectChannel(userDirectChannels);
99
+ // }
100
+ // }, [userChannels?.channelsByUser]);
82
101
 
83
102
  const handleSelectChannel = useCallback((id: any, title: any) => {
84
103
  if (params?.channelId) {
@@ -99,9 +118,9 @@ export interface InboxProps {
99
118
  return (
100
119
  <Box p={2}>
101
120
  <FlatList
102
- data={userDirectChannel?.length > 0 ? userDirectChannel : []}
103
- onRefresh={getChannelsRefetch}
104
- refreshing={userChannelsLoading}
121
+ data={channels && channels?.length > 0 ? channels : []}
122
+ onRefresh={() => setRefresh(true)}
123
+ refreshing={refreshing}
105
124
  contentContainerStyle={{ minHeight: '100%' }}
106
125
  ItemSeparatorComponent={() => <Box height="0.5" backgroundColor="gray.200" />}
107
126
  renderItem={({ item: channel }) => (
@@ -113,21 +132,27 @@ export interface InboxProps {
113
132
  />
114
133
  )}
115
134
  ListEmptyComponent={() => (
116
- <Box p={5}>
117
- <Heading>Chat</Heading>
118
- <Input
119
- height={50}
120
- mt={3}
121
- placeholder="Search"
122
- borderRadius={50}
123
- borderColor={'gray.200'}
124
- borderWidth={2}
125
- />
126
- <Center mt={6}>
127
- <Icon as={Ionicons} name="chatbubbles" size={'xl'} />
128
- <Text>You don't have any messages yet!</Text>
129
- </Center>
130
- </Box>
135
+ <>
136
+ {userChannelsLoading ? (
137
+ <Spinner />
138
+ ) : (
139
+ <Box p={5}>
140
+ <Heading>Chat</Heading>
141
+ <Input
142
+ height={50}
143
+ mt={3}
144
+ placeholder="Search"
145
+ borderRadius={50}
146
+ borderColor={'gray.200'}
147
+ borderWidth={2}
148
+ />
149
+ <Center mt={6}>
150
+ <Icon as={Ionicons} name="chatbubbles" size={'xl'} />
151
+ <Text>You don't have any messages yet!</Text>
152
+ </Center>
153
+ </Box>
154
+ )}
155
+ </>
131
156
  )}
132
157
  keyExtractor={(item, index) => 'key' + index}
133
158
  />