Merge pull request #583 from navi-medici/feedback-whatsapp-share

Feedback whatsapp share
This commit is contained in:
Shri Prakash Bajpai
2023-10-03 20:36:36 +05:30
committed by GitHub
17 changed files with 442 additions and 51 deletions

View File

@@ -30,7 +30,12 @@
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> ->
<uses-permission android:name="android.permission.WAKE_LOCK" />
21 -> lollipop 28 ->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<queries>
<package android:name="com.whatsapp" />
</queries>
<uses-permission android:name="android.permission.USE_EXACT_ALARM"/>
<application
android:name=".MainApplication"
android:label="@string/app_name"
@@ -55,7 +60,17 @@
<service android:name="com.supersami.foregroundservice.ForegroundService" android:foregroundServiceType="location"></service>
<service android:name="com.supersami.foregroundservice.ForegroundServiceTask" android:foregroundServiceType="location"></service>
<activity
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:screenOrientation="portrait"
@@ -74,5 +89,6 @@
<data android:scheme="cosmosapp" android:host="cosmos" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -1,32 +1,59 @@
package com.avapp;
import static android.app.Activity.RESULT_CANCELED;
import static android.app.Activity.RESULT_OK;
import static com.imagepicker.Utils.deleteFile;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.location.LocationManager;
import androidx.annotation.Nullable;
import androidx.core.content.FileProvider;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.BaseActivityEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import android.content.pm.PackageInfo;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Environment;
import android.os.Handler;
import android.util.Base64;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.List;
public class DeviceUtilsModule extends ReactContextBaseJavaModule implements ActivityEventListener {
import android.net.Uri;
import android.util.Log;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class DeviceUtilsModule extends ReactContextBaseJavaModule {
private ReactApplicationContext RNContext;
private File imageFile;
private int WHATSAPP_SHARE_REQUEST_CODE = 12345;
public DeviceUtilsModule(@Nullable ReactApplicationContext reactContext){
super(reactContext);
RNContext = reactContext;
reactContext.addActivityEventListener(mActivityEventListener);
}
@Override
@@ -34,15 +61,22 @@ public class DeviceUtilsModule extends ReactContextBaseJavaModule implements Act
return "DeviceUtilsModule";
}
@Override
public void onActivityResult(Activity activity, int i, int i1, @Nullable Intent intent) {
private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {
@Override
public void onActivityResult(Activity activity, int i, int i1, @Nullable Intent intent) {
if(i1 != RESULT_CANCELED) {
if (i == WHATSAPP_SHARE_REQUEST_CODE) {
deleteFile(Uri.fromFile(imageFile));
}
}
}
}
@Override
public void onNewIntent(Intent intent) {
@Override
public void onNewIntent(Intent intent) {
}
};
}
@ReactMethod
public void isLocationEnabled (Promise promise) {
@@ -77,4 +111,74 @@ public class DeviceUtilsModule extends ReactContextBaseJavaModule implements Act
promise.reject( err);
}
}
private static File convertBase64ToFile(Context context,String base64Data) {
try {
byte[] decodedBytes = Base64.decode(base64Data, Base64.DEFAULT);
File outputDir = context.getCacheDir();
File file = File.createTempFile("temp_image", ".jpg", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS));
FileOutputStream fos = new FileOutputStream(file);
fos.write(decodedBytes);
fos.flush();
fos.close();
return file;
} catch (IOException e) {
Log.e("ShareUtils", "Failed to convert Base64 to file: " + e.getMessage());
return null;
}
}
public boolean isWhatsAppInstalled() {
PackageManager packageManager = RNContext.getPackageManager();
List<PackageInfo> packages = packageManager.getInstalledPackages(PackageManager.GET_META_DATA);
for (PackageInfo packageInfo : packages) {
String packageName = packageInfo.packageName;
if(packageName.equals("com.whatsapp")){
return true;
}
}
return false;
}
@ReactMethod
public void sendFeedbackToWhatsapp(String message, String imageUrl, String mimeType, Promise promise) {
try{
if(!isWhatsAppInstalled()){
promise.reject("errorCode", "1");
return;
}
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, message);
if(imageUrl.equals("")) {
sendIntent.setType("text/plain");
sendIntent.setPackage("com.whatsapp");
getCurrentActivity().startActivity(sendIntent);
} else {
sendIntent.setType(mimeType);
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
imageFile = convertBase64ToFile(getReactApplicationContext(), imageUrl);
Uri fileUri = FileProvider.getUriForFile(getReactApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", new File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
imageFile.getName()
)
);
sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
sendIntent.setPackage("com.whatsapp");
getCurrentActivity().startActivityForResult(sendIntent, WHATSAPP_SHARE_REQUEST_CODE);
}
promise.resolve(true);
return;
} catch (Error e){
promise.reject("errorCode","2");
}
return;
}
}

View File

@@ -3,6 +3,8 @@ package com.avapp;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import android.content.IntentFilter;
import android.os.Bundle;
public class MainActivity extends ReactActivity {
@@ -16,15 +18,6 @@ public class MainActivity extends ReactActivity {
return "AVAPP";
}
// https://github.com/software-mansion/react-native-screens#android
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
}
/**
* Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and
* you can specify the renderer you wish to use - the new renderer (Fabric) or the old renderer

View File

@@ -82,7 +82,8 @@
"react-native-webview": "12.0.2",
"react-redux": "8.0.5",
"redux": "4.2.0",
"redux-persist": "6.0.0"
"redux-persist": "6.0.0",
"rn-fetch-blob": "0.12.0"
},
"devDependencies": {
"@babel/core": "7.12.9",

View File

@@ -0,0 +1,14 @@
import * as React from 'react';
import Svg, { Mask, Path, G } from 'react-native-svg';
const ChevronDown = (props) => {
const { fillColor = '#969696', size = 24 } = props;
return (
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
<Path
d="M11.9958 13.9585C11.907 13.9585 11.8238 13.9445 11.7461 13.9165C11.6684 13.8891 11.5963 13.842 11.5297 13.7755L8.44982 10.6985C8.32774 10.5765 8.26958 10.424 8.27535 10.2408C8.28068 10.0581 8.34439 9.90571 8.46647 9.78374C8.58856 9.66176 8.74394 9.60078 8.93261 9.60078C9.12129 9.60078 9.27667 9.66176 9.39875 9.78374L11.9958 12.3784L14.6095 9.7671C14.7316 9.64513 14.8843 9.58681 15.0677 9.59213C15.2506 9.5979 15.4031 9.66176 15.5252 9.78374C15.6473 9.90571 15.7083 10.0609 15.7083 10.2494C15.7083 10.4379 15.6473 10.5932 15.5252 10.7151L12.462 13.7755C12.3954 13.842 12.3232 13.8891 12.2455 13.9165C12.1678 13.9445 12.0846 13.9585 11.9958 13.9585Z"
fill={fillColor}
/>
</Svg>
);
};
export default ChevronDown;

View File

@@ -0,0 +1,14 @@
import * as React from 'react';
import Svg, { Mask, Path, G } from 'react-native-svg';
const ChevronUp = (props) => {
const { fillColor = '#969696', size = 24 } = props;
return (
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
<Path
d="M8.466 13.767a.63.63 0 0 1-.183-.465.63.63 0 0 1 .183-.465l3.057-3.055a.587.587 0 0 1 .216-.141.742.742 0 0 1 .25-.041c.088 0 .171.013.249.04.077.029.15.076.216.142l3.073 3.072a.609.609 0 0 1 .183.448.637.637 0 0 1-.2.465.63.63 0 0 1-.465.183.63.63 0 0 1-.465-.183l-2.592-2.59-2.608 2.607a.61.61 0 0 1-.449.182.638.638 0 0 1-.465-.199Z"
fill={fillColor}
/>
</Svg>
);
};
export default ChevronUp;

View File

@@ -0,0 +1,11 @@
import * as React from 'react';
import Svg, { Path, SvgProps } from 'react-native-svg';
const SvgComponent = (props: SvgProps) => (
<Svg xmlns="http://www.w3.org/2000/svg" fill="none" {...props}>
<Path
fill="#25D366"
d="M8 1.6A6.4 6.4 0 0 0 1.6 8c0 1.2.337 2.32.912 3.28l-.855 3.12 3.187-.837c.932.53 2.007.837 3.156.837A6.4 6.4 0 0 0 8 1.6ZM5.81 5.014c.103 0 .21 0 .302.004.115.003.239.011.358.274.141.313.449 1.097.488 1.176.04.08.068.173.013.277-.052.107-.08.172-.156.266-.08.092-.167.206-.239.275-.08.08-.162.167-.07.325s.41.678.881 1.097c.606.54 1.117.707 1.275.786.16.08.251.067.343-.04.095-.103.397-.46.503-.619.104-.159.21-.131.354-.08.147.053.926.437 1.085.516.159.08.263.12.303.184.041.066.041.384-.09.754-.132.37-.78.727-1.07.752-.292.027-.565.132-1.903-.395-1.612-.635-2.63-2.287-2.709-2.393-.08-.104-.646-.86-.646-1.638 0-.781.41-1.164.553-1.323a.583.583 0 0 1 .424-.198Z"
/>
</Svg>
);
export default SvgComponent;

View File

@@ -431,6 +431,15 @@ export const CLICKSTREAM_EVENT_NAMES = {
name: 'FA_UNIFIED_ENTITY_REQUEST_FAILED',
description: 'FA_UNIFIED_ENTITY_REQUEST_FAILED',
},
FA_SHARE_FEEDBACK_CLICKED: {
name: 'FA_SHARE_FEEDBACK_CLICKED',
description:
'When user clicks on share feedback on case details screen for any of the filled feedback',
},
FA_SHARE_SUCCESSFUL: {
name: 'FA_SHARE_SUCCESSFUL',
description: 'When user is redirected to WhatsApp after clicking on share feedback',
},
// Notifications
FA_NOTIFICATION_ICON_CLICK: {

View File

@@ -7,3 +7,10 @@ export const locationEnabled = (): Promise<boolean> => DeviceUtilsModule.isLocat
// returns array of all the installed packages.
export const getAllInstalledApp = (): Promise<string> => DeviceUtilsModule.getAllInstalledApp();
// sends feedback data to whatsapp.
export const sendFeedbackToWhatsapp = (
message: string,
imageUrl: string,
mimeType: string
): Promise<boolean> => DeviceUtilsModule.sendFeedbackToWhatsapp(message, imageUrl, mimeType);

View File

@@ -368,3 +368,10 @@ export function getDistanceFromLatLonInKm(
const distance = 2 * Math.atan2(Math.sqrt(intermediateResult), Math.sqrt(1 - intermediateResult));
return EARTH_RADIUS * distance;
}
export function insertCommasinAmount(amount: number | undefined) {
const reversedAmount = amount?.toString().split('').reverse().join('');
const groups = reversedAmount?.match(/.{1,3}/g);
const result = groups?.join(',').split('').reverse().join('');
return result;
}

View File

@@ -77,6 +77,9 @@ export const ToastMessages = {
CASES_DELETION_DISABLED: 'Case deletion is disabled during the generation of visit plan',
GEOLOCATION_COORDINATES_INCORRECT: 'Geolocation not found',
IMAGE_UPLOAD_SUCCESS: 'Your ID card has been sent for approval',
WHATSAPP_FEEDBACK_SHARE_SUCCESS: 'Feedback shared successfully via WhatsApp',
WHATSAPP_FEEDBACK_SHARE_FAILURE: 'Feedback sharing failed via WhatsApp',
WHATSAPP_NOT_INSTALLED: 'WhatsApp is not installed on your device',
};
export enum BOTTOM_TAB_ROUTES {

View File

@@ -215,7 +215,7 @@ const CollectionCaseDetails: React.FC<ICaseDetails> = (props) => {
const commonParams = {
loanAccountNumber: caseDetail.loanAccountNumber,
customerReferenceId: caseDetail.customerReferenceId,
caseId,
caseId: caseId,
};
navigateToScreen(route, { ...params, ...commonParams });
};
@@ -463,6 +463,7 @@ const CollectionCaseDetails: React.FC<ICaseDetails> = (props) => {
<FeedbackListContainer
feedbackList={[...getUnSyncedFeedback(), ...feedbackList]?.splice(0, 5)}
loanAccountNumber={caseDetail?.loanAccountNumber as string}
caseId={caseId}
/>
</View>
</Animated.View>

View File

@@ -25,6 +25,8 @@ import { setFeedbackHistoryLoading } from '../../../reducer/feedbackHistorySlice
import SuspenseLoader from '../../../../RN-UI-LIB/src/components/suspense_loader/SuspenseLoader';
import LineLoader from '../../../../RN-UI-LIB/src/components/suspense_loader/LineLoader';
import NoPastFeedbackIcon from '../../../assets/icons/NoPastFeedbackIcon';
import ChevronDown from '../../../assets/icons/ChevronDown';
import ChevronUp from '../../../assets/icons/ChevronUp';
const FEEDBACK_PAGE_TITLE = 'All feedbacks';
const ADDRESS_FEEDBACK_PAGE_TITLE = 'Address feedback';
@@ -39,13 +41,20 @@ interface IFeedbackDetailContainer {
addressReferenceIds?: string[];
addressText?: string;
activeFeedbackReferenceId?: string;
caseId: string;
};
};
}
const FeedbackDetailContainer: React.FC<IFeedbackDetailContainer> = ({ route: routeParams }) => {
const {
params: { loanAccountNumber, activeFeedbackReferenceId, addressReferenceIds, addressText },
params: {
loanAccountNumber,
activeFeedbackReferenceId,
addressReferenceIds,
addressText,
caseId,
},
} = routeParams;
const isPastFeedbackOnAddress = addressText || addressReferenceIds?.length;
@@ -212,7 +221,12 @@ const FeedbackDetailContainer: React.FC<IFeedbackDetailContainer> = ({ route: ro
}}
>
<Accordion
accordionStyle={[GenericStyles.pv12, GenericStyles.br8, getShadowStyle(4)]}
accordionStyle={[
GenericStyles.br8,
GenericStyles.ph16,
styles.accordianPadding,
getShadowStyle(4),
]}
isActive={feedback.referenceId === activeFeedbackReferenceId}
touchableDelay={50}
touchableOpacity={0.8}
@@ -221,11 +235,12 @@ const FeedbackDetailContainer: React.FC<IFeedbackDetailContainer> = ({ route: ro
key={feedback.referenceId}
feedbackItem={feedback}
isExpanded={isExpanded}
caseId={caseId}
/>
}
customExpandUi={{
whenCollapsed: <Text style={styles.accordionExpandBtn}>View more</Text>,
whenExpanded: <Text style={styles.accordionExpandBtn}>View less</Text>,
whenCollapsed: <ChevronDown />,
whenExpanded: <ChevronUp />,
}}
onExpanded={(value) => {
setIsExpanded(value);
@@ -321,6 +336,10 @@ const styles = StyleSheet.create({
lineHeight: 20,
color: COLORS.TEXT.BLUE,
},
accordianPadding: {
paddingTop: 16,
paddingBottom: 8,
},
});
export default FeedbackDetailContainer;

View File

@@ -1,5 +1,5 @@
import React, { ReactNode } from 'react';
import { View, StyleSheet, TouchableOpacity, Linking } from 'react-native';
import React, { ReactNode, useEffect, useState } from 'react';
import { View, StyleSheet, TouchableOpacity, Linking, Platform, NativeModules } from 'react-native';
import Text from '../../../../RN-UI-LIB/src/components/Text';
import { GenericStyles } from '../../../../RN-UI-LIB/src/styles';
import { COLORS } from '../../../../RN-UI-LIB/src/styles/colors';
@@ -8,25 +8,42 @@ import {
BUSINESS_TIME_FORMAT,
dateFormat,
} from '../../../../RN-UI-LIB/src/utlis/dates';
import { getGoogleMapUrl, sanitizeString } from '../../../components/utlis/commonFunctions';
import { FIELD_FEEDBACKS, ICallingFeedback, IFeedback } from '../../../types/feedback.types';
import { Address as IAddress } from '../interface';
import {
getGoogleMapUrl,
insertCommasinAmount,
sanitizeString,
} from '../../../components/utlis/commonFunctions';
import {
FIELD_FEEDBACKS,
ICallingFeedback,
IFeedback,
OPTION_TAG,
} from '../../../types/feedback.types';
import { CaseDetail, Address as IAddress } from '../interface';
import MapIcon from '../../../../RN-UI-LIB/src/Icons/MapIcon';
import { FEEDBACK_TYPE } from '../../../types/feedback.types';
import CallIcon from '../../../../RN-UI-LIB/src/Icons/CallIcon';
import { addClickstreamEvent } from '../../../services/clickstreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '../../../common/Constants';
import IconLabel from '../../../common/IconLabel';
import WhatsAppFeedbackShareIcon from '../../../assets/icons/WhatsAppIcon';
import ReactNativeBlobUtil from 'react-native-blob-util';
import { useAppSelector } from '../../../hooks';
import { toast } from '../../../../RN-UI-LIB/src/components/toast';
import { ToastMessages } from '../../allCases/constants';
import { sendFeedbackToWhatsapp } from '../../../components/utlis/DeviceUtils';
interface IFeedbackDetailItem {
feedbackItem: IFeedback;
isExpanded: boolean;
caseId: string;
}
const feedbackTypeIcon: Record<FEEDBACK_TYPE, ReactNode> = {
FIELD_VISIT: <MapIcon />,
INHOUSE_FIELD_VISIT: <MapIcon />,
SELF_CALL: <CallIcon fillColor={COLORS.TEXT.BLUE} />,
CALL_BRIDGE: <CallIcon fillColor={COLORS.TEXT.BLUE} />,
FIELD_VISIT: <MapIcon fillColor={COLORS.TEXT.LIGHT} />,
INHOUSE_FIELD_VISIT: <MapIcon fillColor={COLORS.TEXT.LIGHT} />,
SELF_CALL: <CallIcon fillColor={COLORS.TEXT.LIGHT} />,
CALL_BRIDGE: <CallIcon fillColor={COLORS.TEXT.LIGHT} />,
};
const getAddress = (address?: IAddress) => {
@@ -47,9 +64,123 @@ const openGeolocation = (latitude: string, longitude: string) => {
return Linking.openURL(geolocationUrl);
};
const FeedbackDetailItem = ({ feedbackItem, isExpanded }: IFeedbackDetailItem) => {
function getLocationLink(latitude: string, longitude: string): string {
const link = 'https://www.google.com/maps/search/?api=1&query=' + latitude + ',' + longitude;
return link;
}
const sendToWhatsappNative = (
message: string,
imageUrl: string,
mimeType: string,
caseDetails: CaseDetail,
agentId: string
) => {
sendFeedbackToWhatsapp(message, imageUrl, mimeType)
.then((res: boolean) => {
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_SHARE_SUCCESSFUL, {
caseId: caseDetails?.id,
agentId: agentId,
});
})
.catch((err: Error) => {
if (err.message === '1') {
toast({
text1: ToastMessages.WHATSAPP_NOT_INSTALLED,
type: 'error',
});
} else {
toast({
text1: ToastMessages.WHATSAPP_FEEDBACK_SHARE_FAILURE,
type: 'error',
});
}
});
};
const sendToWhatsapp = (feedbackItem: IFeedback, caseDetails: CaseDetail, agentId: string) => {
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_SHARE_FEEDBACK_CLICKED, {
caseId: caseDetails?.id,
agentId: agentId,
});
var message = `*Visit Feedback* for ${sanitizeString(caseDetails?.customerName)}
_${sanitizeString(dateFormat(new Date(feedbackItem?.createdAt), 'DD MMM, YYYY | HH:mm a.'))}_\n
*LAN*: ${sanitizeString(caseDetails?.loanAccountNumber)}
*DPD Bucket*: ${sanitizeString(caseDetails?.dpdBucket)}
*EMI Amount*: ₹${insertCommasinAmount(caseDetails?.outstandingEmiDetails?.[0]?.emiAmount)}\n
*Disposition*: ${sanitizeString(feedbackItem?.interactionStatus)}`;
const ptpDate = feedbackItem?.answerViews.filter((answer) => answer.questionName === 'PTP Date');
if (ptpDate.length > 0) {
message +=
' for ' + sanitizeString(dateFormat(new Date(ptpDate[0].inputDate), 'DD MMM, YYYY')) + '\n\n';
} else {
message += '\n\n';
}
message += '*Remarks*: ';
const answerList = feedbackItem?.answerViews?.filter(
(answer) => answer.questionName === 'Comments'
);
if (answerList.length > 0) {
message += sanitizeString(answerList[0]?.inputText) + '\n\n';
} else {
message += 'N.A.\n\n';
}
{
feedbackItem?.metadata?.interactionLatitude &&
feedbackItem?.metadata?.interactionLongitude &&
FIELD_FEEDBACKS.includes(feedbackItem?.type)
? (message +=
'*Location of feedback*: ' +
sanitizeString(
getLocationLink(
feedbackItem?.metadata?.interactionLatitude,
feedbackItem?.metadata?.interactionLongitude
)
) +
'\n\n')
: null;
}
const imagesList = feedbackItem?.answerViews.filter(
(answer) => answer.questionTag === OPTION_TAG.IMAGE_UPLOAD
);
var imageUrl = '';
const mimeType = 'image/*';
if (imagesList.length > 0) {
var imageUri = '';
imageUri = imagesList[0]?.inputText ? imagesList[0].inputText : '';
let imagePath = '';
ReactNativeBlobUtil.config({
fileCache: true,
})
.fetch('GET', imageUri)
.then((resp: any) => {
if (resp.info().status !== 200) {
return '';
} else {
imagePath = resp.path();
return resp.readFile('base64');
}
})
.then((base64Data: any) => {
imageUrl = base64Data;
sendToWhatsappNative(message, imageUrl, mimeType, caseDetails, agentId);
ReactNativeBlobUtil.fs.unlink(imagePath);
});
} else {
sendToWhatsappNative(message, imageUrl, mimeType, caseDetails, agentId);
}
};
const FeedbackDetailItem = ({ feedbackItem, isExpanded, caseId }: IFeedbackDetailItem) => {
const caseDetails = useAppSelector((state) => state.allCases.caseDetails[caseId]);
const { agentId } = useAppSelector((state) => ({ agentId: state.user.user?.referenceId!! }));
return (
<View style={[GenericStyles.ph8]}>
<View style={[styles.addressItem]}>
<View style={[GenericStyles.row, GenericStyles.alignCenter]}>
{feedbackTypeIcon[feedbackItem.type] ? (
<View style={GenericStyles.mr8}>{feedbackTypeIcon[feedbackItem.type]}</View>
@@ -70,13 +201,17 @@ const FeedbackDetailItem = ({ feedbackItem, isExpanded }: IFeedbackDetailItem) =
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={[styles.textContainer, styles.cardFooterText, GenericStyles.fontSize12]}
style={[
styles.textContainer,
styles.cardFooterText,
GenericStyles.fontSize12,
GenericStyles.mb12,
]}
>
{sanitizeString(dateFormat(new Date(feedbackItem.createdAt), BUSINESS_DATE_FORMAT))}
&nbsp;&nbsp;&#9679;&nbsp;&nbsp;
<Text style={styles.bullet}> </Text>
{sanitizeString(dateFormat(new Date(feedbackItem.createdAt), BUSINESS_TIME_FORMAT))}
</Text>
<View style={[GenericStyles.borderTop, GenericStyles.w100, GenericStyles.mv16]} />
<Text
numberOfLines={isExpanded ? 100 : 3}
ellipsizeMode="tail"
@@ -93,18 +228,33 @@ const FeedbackDetailItem = ({ feedbackItem, isExpanded }: IFeedbackDetailItem) =
</Text>
{feedbackItem.metadata?.interactionLatitude && FIELD_FEEDBACKS.includes(feedbackItem.type) ? (
<TouchableOpacity
activeOpacity={0.7}
onPress={() =>
openGeolocation(
feedbackItem.metadata?.interactionLatitude,
feedbackItem.metadata?.interactionLongitude
)
}
style={[GenericStyles.row, GenericStyles.pv12]}
>
<Text style={[styles.textContainer, styles.geolocationBtn]}>Open map</Text>
</TouchableOpacity>
<>
<View style={styles.container}>
<TouchableOpacity
activeOpacity={0.7}
onPress={() =>
openGeolocation(
feedbackItem.metadata?.interactionLatitude,
feedbackItem.metadata?.interactionLongitude
)
}
style={[GenericStyles.row, styles.BtnPadding]}
>
<Text style={[styles.textContainer, styles.geolocationBtn]}>Open map</Text>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.7}
onPress={() => sendToWhatsapp(feedbackItem, caseDetails, agentId)}
style={[GenericStyles.row, styles.BtnPadding]}
>
<IconLabel
text="Share"
icon={<WhatsAppFeedbackShareIcon />}
textStyle={{ color: COLORS.BASE.BLUE }}
/>
</TouchableOpacity>
</View>
</>
) : null}
</View>
);
@@ -121,7 +271,7 @@ const styles = StyleSheet.create({
},
cardLightTitle: {
fontWeight: '400',
color: '#BCBCBC',
color: COLORS.TEXT.BLACK,
},
cardFooterText: {
fontWeight: '400',
@@ -129,6 +279,22 @@ const styles = StyleSheet.create({
},
geolocationBtn: {
color: COLORS.BASE.BLUE,
marginRight: 20,
},
container: {
flexDirection: 'row',
alignItems: 'center',
marginTop: 0,
},
bullet: {
color: COLORS.TEXT.GREY_1,
},
BtnPadding: {
paddingTop: 8,
paddingBottom: 8,
},
addressItem: {
paddingHorizontal: 0,
},
});

View File

@@ -18,6 +18,7 @@ import { getCaseUnifiedData, UnifiedCaseDetailsTypes } from '../../../action/cas
interface IFeedbackListContainer {
loanAccountNumber: string;
feedbackList: (IFeedback | IUnSyncedFeedbackItem)[];
caseId: string;
}
interface IOfflineFeedbackListContainer {
@@ -66,6 +67,7 @@ const OfflineFeedbackListContainer: React.FC<IOfflineFeedbackListContainer> = ({
const FeedbackListContainer: React.FC<IFeedbackListContainer> = ({
loanAccountNumber,
feedbackList,
caseId,
}) => {
const [retryBtnCount, setRetryBtnCount] = useState(0);
@@ -97,6 +99,7 @@ const FeedbackListContainer: React.FC<IFeedbackListContainer> = ({
feedbackItem={feedbackItem}
loanAccountNumber={loanAccountNumber}
showHorizontalLine={++idx !== feedbackList.length}
caseId={caseId}
/>
))}
</View>

View File

@@ -15,11 +15,13 @@ interface IFeedbackListItem {
feedbackItem: IFeedback | IUnSyncedFeedbackItem;
showHorizontalLine?: boolean;
loanAccountNumber: string;
caseId: string;
}
const FeedbackListItem: React.FC<IFeedbackListItem> = ({
feedbackItem,
loanAccountNumber,
caseId,
showHorizontalLine = true,
}) => {
const handleRouting = (route: PageRouteEnum, params: object | undefined = undefined) => {
@@ -27,6 +29,7 @@ const FeedbackListItem: React.FC<IFeedbackListItem> = ({
const commonParams = {
loanAccountNumber,
activeFeedbackReferenceId: (feedbackItem as IFeedback).referenceId,
caseId: caseId,
};
navigateToScreen(route, { ...params, ...commonParams });
};

View File

@@ -4705,6 +4705,18 @@ glob@5.0.15:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@7.0.6:
version "7.0.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a"
integrity sha512-f8c0rE8JiCxpa52kWPAOa3ZaYEnzofDzCQLCn3Vdk0Z5OVLq3BsRFJI4S4ykpeVW6QMGBUkMeUpoEgWnMTnw5Q==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.2"
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@7.1.6:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
@@ -8236,6 +8248,14 @@ rimraf@~2.6.2:
dependencies:
glob "^7.1.3"
rn-fetch-blob@0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/rn-fetch-blob/-/rn-fetch-blob-0.12.0.tgz#ec610d2f9b3f1065556b58ab9c106eeb256f3cba"
integrity sha512-+QnR7AsJ14zqpVVUbzbtAjq0iI8c9tCg49tIoKO2ezjzRunN7YL6zFSFSWZm6d+mE/l9r+OeDM3jmb2tBb2WbA==
dependencies:
base-64 "0.1.0"
glob "7.0.6"
route-recognizer@^0.3.3:
version "0.3.4"
resolved "https://registry.yarnpkg.com/route-recognizer/-/route-recognizer-0.3.4.tgz#39ab1ffbce1c59e6d2bdca416f0932611e4f3ca3"