From 25296101ca399501f08189641ba0ebae2e433b11 Mon Sep 17 00:00:00 2001 From: Khairul Alam Licon Date: Thu, 22 Dec 2022 14:09:35 +0900 Subject: [PATCH] [MINI-5825][MINI-5829] Supports multiple emails: SDK & JS Sample app changes (#236) * add changes for bridge * add sample app change * tabs implementation * circleci fix attempt --- js-miniapp-bridge/src/types/contact.ts | 1 + js-miniapp-bridge/test/test.spec.ts | 3 +- js-miniapp-sample/src/pages/user-details.js | 516 ++++++++++++-------- js-miniapp-sdk/CHANGELOG.md | 1 + 4 files changed, 305 insertions(+), 216 deletions(-) diff --git a/js-miniapp-bridge/src/types/contact.ts b/js-miniapp-bridge/src/types/contact.ts index 17be631a9..7e99cdcd3 100644 --- a/js-miniapp-bridge/src/types/contact.ts +++ b/js-miniapp-bridge/src/types/contact.ts @@ -3,4 +3,5 @@ export interface Contact { id: string; name?: string; email?: string; + allEmailList?: string[]; } diff --git a/js-miniapp-bridge/test/test.spec.ts b/js-miniapp-bridge/test/test.spec.ts index d944515e2..e65abf873 100644 --- a/js-miniapp-bridge/test/test.spec.ts +++ b/js-miniapp-bridge/test/test.spec.ts @@ -623,7 +623,7 @@ describe('getContacts', () => { it('will return the close status string response', () => { const bridge = new Bridge.MiniAppBridge(mockExecutor); const response = - '[{"id":"id_contact","name":"Cory","email":"cory@miniapp.com"},{"id":"id_contact2","name":"Alam"},{"id":"id_contact3"}]'; + '[{"id":"id_contact","name":"Cory","email":"cory@miniapp.com","allEmailList":["another1@miniapp.com", "another2@miniapp.com"]},{"id":"id_contact2","name":"Alam"},{"id":"id_contact3"}]'; mockExecutor.exec.callsArgWith(2, response); const expected = [ @@ -631,6 +631,7 @@ describe('getContacts', () => { id: 'id_contact', name: 'Cory', email: 'cory@miniapp.com', + allEmailList: ['another1@miniapp.com', 'another2@miniapp.com'], }, { id: 'id_contact2', diff --git a/js-miniapp-sample/src/pages/user-details.js b/js-miniapp-sample/src/pages/user-details.js index edbf7e814..7dc9c7472 100644 --- a/js-miniapp-sample/src/pages/user-details.js +++ b/js-miniapp-sample/src/pages/user-details.js @@ -4,11 +4,10 @@ import { Avatar, Button, CardHeader, + Container, CircularProgress, FormGroup, Typography, - CardContent, - CardActions, List, ListItem, ListItemAvatar, @@ -18,6 +17,10 @@ import { } from '@material-ui/core'; import { red, green } from '@material-ui/core/colors'; import { makeStyles } from '@material-ui/core/styles'; +import Tab from '@material-ui/core/Tab'; +import TabContext from '@material-ui/lab/TabContext'; +import TabList from '@material-ui/lab/TabList'; +import TabPanel from '@material-ui/lab/TabPanel'; import clsx from 'clsx'; import { CustomPermission, @@ -29,7 +32,6 @@ import { } from 'js-miniapp-sdk'; import { connect } from 'react-redux'; -import GreyCard from '../components/GreyCard'; import { requestCustomPermissions } from '../services/permissions/actions'; import { requestContactList, @@ -54,6 +56,12 @@ const useStyles = makeStyles((theme) => ({ width: '85vw', maxWidth: 500, }, + wrapperContainer: { + height: '100%', + display: 'flex', + flexDirection: 'column', + paddingLeft: 0, + }, wrapper: { position: 'relative', marginTop: 10, @@ -116,30 +124,43 @@ const useStyles = makeStyles((theme) => ({ profilePhoto: { height: 100, width: 100, - marginBottom: 20, }, - contactsList: { + profilePhotoOuter: { + display: 'flex', + justifyContent: 'center', + }, + userProfile: { maxHeight: 125, overflow: 'auto', }, + contactsList: { + maxHeight: 320, + overflow: 'auto', + }, red: { color: red[500], }, })); export const initialState = { - isLoading: false, - isError: false, - hasRequestedPermissions: false, + isNamePhotoLoading: false, + isNamePhottoError: false, + hasRequestedNamePhotoPermissions: false, + isContactsLoading: false, + isContactsError: false, + hasRequestedContactsPermissions: false, isPointsLoading: false, isPointsError: false, hasRequestedPointPermissions: false, }; type State = { - isLoading: ?boolean, - isError: ?boolean, - hasRequestedPermissions: boolean, + isNamePhotoLoading: ?boolean, + isNamePhotoError: ?boolean, + hasRequestedNamePhotoPermissions: boolean, + isContactsLoading: ?boolean, + isContactsError: ?boolean, + hasRequestedContactsPermissions: boolean, isPointsLoading: ?boolean, isPointsError: ?boolean, hasRequestedPointPermissions: boolean, @@ -151,27 +172,46 @@ type Action = { export const dataFetchReducer = (state: State, action: Action) => { switch (action.type) { - case 'FETCH_INIT': + case 'NAME_PHOTO_FETCH_INIT': return { ...state, - isLoading: true, - isError: false, - hasRequestedPermissions: false, + isNamePhotoLoading: true, + isNamePhotoError: false, + hasRequestedNamePhotoPermissions: false, }; - case 'FETCH_SUCCESS': + case 'NAME_PHOTO_FETCH_SUCCESS': return { ...state, - isLoading: false, - isError: false, - hasRequestedPermissions: true, + isNamePhotoLoading: false, + isNamePhotoError: false, + hasRequestedNamePhotoPermissions: true, }; - case 'FETCH_FAILURE': + case 'NAME_PHOTO_FETCH_FAILURE': return { ...initialState, - isLoading: false, - isError: true, + isNamePhotoLoading: false, + isNamePhotoError: true, + }; + case 'CONTACTS_FETCH_INIT': + return { + ...state, + isContactsLoading: true, + isContactsError: false, + hasRequestedContactsPermissions: false, + }; + case 'CONTACTS_FETCH_SUCCESS': + return { + ...state, + isContactsLoading: false, + isContactsError: false, + hasRequestedContactsPermissions: true, + }; + case 'CONTACTS_FETCH_FAILURE': + return { + ...initialState, + isContactsLoading: false, + isContactsError: true, }; - case 'POINTS_FETCH_INIT': return { ...state, @@ -192,7 +232,6 @@ export const dataFetchReducer = (state: State, action: Action) => { isPointsLoading: false, isPointsError: true, }; - default: throw Error('Unknown action type'); } @@ -217,7 +256,8 @@ function UserDetails(props: UserDetailsProps) { const [state, dispatch] = useReducer(dataFetchReducer, initialState); const classes = useStyles(); - const userDetailsButtonClassname = getButtonState(state.isError); + const namePhotoButtonClassname = getButtonState(state.isNamePhotoError); + const contactsButtonClassname = getButtonState(state.isContactsError); const pointsButtonClassname = getButtonState(state.isPointsError); function getButtonState(isError: boolean) { @@ -227,7 +267,7 @@ function UserDetails(props: UserDetailsProps) { }); } - function requestUserDetails() { + function requestNamePhoto() { const permissionsList = [ { name: CustomPermissionName.USER_NAME, @@ -239,10 +279,6 @@ function UserDetails(props: UserDetailsProps) { description: 'We would like to display your Profile Photo on your profile page.', }, - { - name: CustomPermissionName.CONTACT_LIST, - description: 'We would like to send messages to your contacts.', - }, ]; props @@ -256,15 +292,37 @@ function UserDetails(props: UserDetailsProps) { hasPermission(CustomPermissionName.PROFILE_PHOTO, permissions) ? props.getProfilePhoto() : null, + ]) + ) + .then(() => dispatch({ type: 'NAME_PHOTO_FETCH_SUCCESS' })) + .catch((e) => { + console.error(e); + dispatch({ type: 'NAME_PHOTO_FETCH_FAILURE' }); + }); + } + + function requestContacts() { + const permissionsList = [ + { + name: CustomPermissionName.CONTACT_LIST, + description: 'We would like to send messages to your contacts.', + }, + ]; + + props + .requestPermissions(permissionsList) + .then((permissions) => filterAllowedPermissions(permissions)) + .then((permissions) => + Promise.all([ hasPermission(CustomPermissionName.CONTACT_LIST, permissions) ? props.getContacts() : null, ]) ) - .then(() => dispatch({ type: 'FETCH_SUCCESS' })) + .then(() => dispatch({ type: 'CONTACTS_FETCH_SUCCESS' })) .catch((e) => { console.error(e); - dispatch({ type: 'FETCH_FAILURE' }); + dispatch({ type: 'CONTACTS_FETCH_FAILURE' }); }); } @@ -302,196 +360,177 @@ function UserDetails(props: UserDetailsProps) { .map((permission) => permission.name); } - function handleClick(e) { - if (!state.isLoading) { + function handleNamePhotoClick(e) { + if (!state.isNamePhotoLoading) { + e.preventDefault(); + dispatch({ type: 'NAME_PHOTO_FETCH_INIT' }); + requestNamePhoto(); + } + } + + function handleContactsClick(e) { + if (!state.isContactsLoading) { e.preventDefault(); - dispatch({ type: 'FETCH_INIT' }); - requestUserDetails(); + dispatch({ type: 'CONTACTS_FETCH_INIT' }); + requestContacts(); } } function handlePointsClick(e) { - if (!state.isLoading) { + if (!state.isPointsLoading) { e.preventDefault(); dispatch({ type: 'POINTS_FETCH_INIT' }); requestPoints(); } } - function ProfilePhoto() { - const hasDeniedPermission = - state.hasRequestedPermissions && + function CardNamePhotoActionsForm() { + const hasPhotoDeniedPermission = + state.hasRequestedNamePhotoPermissions && !hasPermission(CustomPermissionName.PROFILE_PHOTO); - return [ - hasDeniedPermission ? ( - - ) : null, - , - ]; - } - - function UserDetails() { - const hasDeniedPermission = - state.hasRequestedPermissions && + const hasNameDeniedPermission = + state.hasRequestedNamePhotoPermissions && !hasPermission(CustomPermissionName.USER_NAME); return ( - - - - - ); - } - - function ContactList() { - const hasDeniedPermision = - state.hasRequestedPermissions && - !hasPermission(CustomPermissionName.CONTACT_LIST); - - return ( - - - - {hasDeniedPermision && ( - - - - )} - {!hasDeniedPermision && - props.contactList && - props.contactList.map((contact) => ( - - - - + + + + {hasPhotoDeniedPermission && ( + - - {contact.name && contact.name !== '' && ( - {'Name: ' + contact.name} - )} - - - {contact.email && contact.email !== '' && ( - {'Email: ' + contact.email} - )} - - - } + primary='"Profile Photo" permission not granted.' + className={classes.red} + key="avatar-error" /> - ))} - - + )} + {!hasPhotoDeniedPermission && ( +
+ +
+ )} +
+ + +
+
+ + {state.isNamePhotoLoading && ( + + )} +
+ {state.isNamePhotoError && ( + + Error fetching the user name and photo + + )} + ); } - function PointBalance() { - const hasDeniedPermission = - state.hasRequestedPointPermissions && - !hasPermission(CustomPermissionName.POINTS); - - return ( - - - - - - - ); - } + function CardContactsActionsForm() { + const hasDeniedPermision = + state.hasRequestedContactsPermissions && + !hasPermission(CustomPermissionName.CONTACT_LIST); - function CardActionsForm() { return (
- {state.isLoading && ( + {state.isContactsLoading && ( )}
- {state.isError && ( + + + + {hasDeniedPermision && ( + + + + )} + {!hasDeniedPermision && + props.contactList && + props.contactList.map((contact) => ( + + + + + + + {contact.name && contact.name !== '' && ( + {'Name: ' + contact.name} + )} + + + {contact.email && contact.email !== '' && ( + {'Email: ' + contact.email} + )} + + + {contact.allEmailList && + contact.allEmailList.length > 0 && ( + + {'Email list: ' + + contact.allEmailList.join(', ')} + + )} + + + } + /> + + ))} + + + + {state.isContactsError && ( - Error fetching the User Details + Error fetching the contacts )}
@@ -499,8 +538,62 @@ function UserDetails(props: UserDetailsProps) { } function CardPointActionsForm() { + const hasDeniedPermission = + state.hasRequestedPointPermissions && + !hasPermission(CustomPermissionName.POINTS); + return ( + + + + + + +