From e2ef88fcb7b4491853c127f908fd4594c9b005f1 Mon Sep 17 00:00:00 2001 From: noumantahir Date: Fri, 10 Sep 2021 23:07:23 +0500 Subject: [PATCH 01/16] added basic quick profile placeholder and fetching basic user data --- src/components/index.js | 2 + src/components/organisms/index.ts | 1 + .../children/quickProfileContent.tsx | 21 +++ .../children/quickProfileStyles.ts | 80 +++++++++++ .../container/quickProfileModal.tsx | 127 ++++++++++++++++++ .../organisms/quickProfileModal/index.ts | 1 + .../postCard/container/postCardContainer.js | 44 +++--- src/redux/actions/uiAction.ts | 16 +++ src/redux/constants/constants.js | 2 + src/redux/reducers/uiReducer.ts | 22 ++- .../application/screen/applicationScreen.js | 2 + 11 files changed, 296 insertions(+), 22 deletions(-) create mode 100644 src/components/organisms/index.ts create mode 100644 src/components/organisms/quickProfileModal/children/quickProfileContent.tsx create mode 100644 src/components/organisms/quickProfileModal/children/quickProfileStyles.ts create mode 100644 src/components/organisms/quickProfileModal/container/quickProfileModal.tsx create mode 100644 src/components/organisms/quickProfileModal/index.ts diff --git a/src/components/index.js b/src/components/index.js index ab033ece3..746d80704 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -91,6 +91,7 @@ import { ActionModal } from './actionModal'; import { CustomiseFiltersModal } from './customiseFiltersModal'; import { ForegroundNotification } from './foregroundNotification'; import { PostHtmlRenderer } from './postHtmlRenderer'; +import { QuickProfileModal } from './organisms'; // Basic UI Elements import { @@ -230,4 +231,5 @@ export { CustomiseFiltersModal, ForegroundNotification, PostHtmlRenderer, + QuickProfileModal, }; diff --git a/src/components/organisms/index.ts b/src/components/organisms/index.ts new file mode 100644 index 000000000..839b779e0 --- /dev/null +++ b/src/components/organisms/index.ts @@ -0,0 +1 @@ +export * from './quickProfileModal'; \ No newline at end of file diff --git a/src/components/organisms/quickProfileModal/children/quickProfileContent.tsx b/src/components/organisms/quickProfileModal/children/quickProfileContent.tsx new file mode 100644 index 000000000..3adafbae3 --- /dev/null +++ b/src/components/organisms/quickProfileModal/children/quickProfileContent.tsx @@ -0,0 +1,21 @@ +import React from 'react' +import { View, Text, ActivityIndicator } from 'react-native' +import styles from './quickProfileStyles' + +interface QuickProfileContentProps { + isLoading:boolean, + username:string +} + +export const QuickProfileContent = ({ + isLoading, + username +}:QuickProfileContentProps) => { + return ( + + Profile content goes here + {username} + {isLoading && } + + ) +} diff --git a/src/components/organisms/quickProfileModal/children/quickProfileStyles.ts b/src/components/organisms/quickProfileModal/children/quickProfileStyles.ts new file mode 100644 index 000000000..12f2d2c3d --- /dev/null +++ b/src/components/organisms/quickProfileModal/children/quickProfileStyles.ts @@ -0,0 +1,80 @@ +import { TextStyle, ViewStyle, ImageStyle } from 'react-native'; +import EStyleSheet from 'react-native-extended-stylesheet'; +import { getBottomSpace } from 'react-native-iphone-x-helper'; + +export default EStyleSheet.create({ + modalStyle: { + backgroundColor: '$primaryBackgroundColor', + margin:0, + paddingTop:32, + paddingBottom: getBottomSpace() + 8, + }, + + sheetContent: { + backgroundColor: '$primaryBackgroundColor', + position:'absolute', + bottom:0, + left:0, + right:0, + zIndex:999 + }, + + container:{ + marginTop:16, + marginBottom:44, + paddingHorizontal:24, + alignItems:'center', + justifyContent:'space-between', + } as ViewStyle, + + imageStyle:{ + marginTop:8, + height:150, + width:'100%', + } as ImageStyle, + + textContainer:{ + marginTop:32, + marginBottom:44, + } as ViewStyle, + + title: { + color: '$primaryBlack', + alignSelf: 'center', + textAlign: 'center', + fontSize: 20, + fontWeight: '800', + } as TextStyle, + + bodyText: { + color: '$primaryBlack', + alignSelf: 'center', + textAlign: 'center', + fontSize: 16, + fontWeight: '600', + marginTop:4, + } as TextStyle, + + btnText:{ + color:'$pureWhite' + } as TextStyle, + + button:{ + + backgroundColor:'$primaryBlue', + width:150, + paddingVertical:16, + borderRadius:32, + justifyContent:'center', + alignItems:'center' + } as ViewStyle, + + + actionPanel:{ + width:'100%', + flexDirection:'row', + justifyContent:'space-around', + alignItems:'center', + } as ViewStyle, + +}) \ No newline at end of file diff --git a/src/components/organisms/quickProfileModal/container/quickProfileModal.tsx b/src/components/organisms/quickProfileModal/container/quickProfileModal.tsx new file mode 100644 index 000000000..3da8ec5ec --- /dev/null +++ b/src/components/organisms/quickProfileModal/container/quickProfileModal.tsx @@ -0,0 +1,127 @@ +import React, { useEffect, useRef, useState } from 'react'; +import {Alert, AlertButton } from 'react-native'; +import { Source } from 'react-native-fast-image'; +import ActionSheet from 'react-native-actions-sheet'; +import { QuickProfileContent } from '../children/quickProfileContent'; +import EStyleSheet from 'react-native-extended-stylesheet'; +import styles from '../children/quickProfileStyles'; +import { useAppDispatch, useAppSelector } from '../../../../hooks'; +import { hideProfileModal } from '../../../../redux/actions/uiAction'; +import { getFollows, getRelationship, getUser } from '../../../../providers/hive/dhive'; +import { checkFavorite } from '../../../../providers/ecency/ecency'; +import { useIntl } from 'react-intl'; + + + +export interface ActionModalData { + title:string, + body:string, + buttons:AlertButton[], + headerImage?:Source, + onClosed:()=>void, +} + + +export const QuickProfileModal = ({}) => { + const intl = useIntl(); + const sheetModalRef = useRef(); + const dispatch = useAppDispatch(); + + const profileModalUsername = useAppSelector((state)=>state.ui.profileModalUsername); + const currentAccount = useAppSelector((state)=>state.account.currentAccount); + + const [follows, setFollows] = useState(null); + const [user, setUser] = useState(null); + const [isFollowing, setIsFollowing] = useState(false); + const [isMuted, setIsMuted] = useState(false); + const [isFavourite, setIsFavourite] = useState(false); + const [isLoading, setIsLoading] = useState(false); + + const isOwnProfile = currentAccount.name === profileModalUsername; + + useEffect(() => { + if(profileModalUsername){ + _fetchUser() + _fetchProfile() + sheetModalRef.current.show(); + } + }, [profileModalUsername]) + + + const _fetchUser = async () => { + setIsLoading(true); + let user; + + try { + user = await getUser(profileModalUsername, isOwnProfile); + setUser(user) + } catch (error) { + setIsLoading(false); + } + }; + + + const _fetchProfile = async () => { + const username = profileModalUsername; + try { + if (username) { + + let _isFollowing; + let _isMuted; + let _isFavourite; + let follows; + + if (!isOwnProfile) { + const res = await getRelationship(currentAccount.name, username); + _isFollowing = res && res.follows; + _isMuted = res && res.ignores; + _isFavourite = await checkFavorite(username); + } + + try { + follows = await getFollows(username); + } catch (err) { + follows = null; + } + + + setFollows(follows); + setIsFollowing(_isFollowing); + setIsMuted(_isMuted) + setIsFavourite(_isFavourite) + setIsLoading(false); + + } + } catch (error) { + console.warn('Failed to fetch complete profile data', error); + Alert.alert( + intl.formatMessage({ + id: 'alert.fail', + }), + error.message || error.toString(), + ); + } + }; + + + + const _onClose = () => { + dispatch(hideProfileModal()); + } + + + return ( + + + + + ); +}; diff --git a/src/components/organisms/quickProfileModal/index.ts b/src/components/organisms/quickProfileModal/index.ts new file mode 100644 index 000000000..d14f44d9e --- /dev/null +++ b/src/components/organisms/quickProfileModal/index.ts @@ -0,0 +1 @@ +export * from './container/quickProfileModal'; \ No newline at end of file diff --git a/src/components/postCard/container/postCardContainer.js b/src/components/postCard/container/postCardContainer.js index 621013a00..534c4152f 100644 --- a/src/components/postCard/container/postCardContainer.js +++ b/src/components/postCard/container/postCardContainer.js @@ -4,16 +4,15 @@ import { connect } from 'react-redux'; import get from 'lodash/get'; // Services -import { act } from 'react-test-renderer'; -import { getPost, getActiveVotes } from '../../../providers/hive/dhive'; +import { getPost } from '../../../providers/hive/dhive'; import { getPostReblogs } from '../../../providers/ecency/ecency'; -import { parseActiveVotes } from '../../../utils/postParser'; - import PostCardView from '../view/postCardView'; // Constants import { default as ROUTES } from '../../../constants/routeNames'; +import { useAppDispatch } from '../../../hooks'; +import { showProfileModal } from '../../../redux/actions/uiAction'; /* * Props Name Description Value *@props --> props name here description here Value Type Here @@ -31,6 +30,8 @@ const PostCardContainer = ({ setImageHeight, pageType, }) => { + const dispatch = useAppDispatch(); + const [_content, setContent] = useState(content); const [reblogs, setReblogs] = useState([]); const activeVotes = get(_content, 'active_votes', []); @@ -78,23 +79,26 @@ const PostCardContainer = ({ const _handleOnUserPress = (username) => { if (_content) { - let params = { - username: username || get(_content, 'author'), - reputation: !username && get(_content, 'author_reputation'), - }; + username = username || get(_content, 'author'); + // let params = { + // username: username || get(_content, 'author'), + // reputation: !username && get(_content, 'author_reputation'), + // }; - if ( - get(currentAccount, 'name') === params.username && - (pageType === 'main' || pageType === 'ownProfile') - ) { - navigation.navigate(ROUTES.TABBAR.PROFILE); - } else { - navigation.navigate({ - routeName: ROUTES.SCREENS.PROFILE, - params, - key: get(_content, 'author'), - }); - } + dispatch(showProfileModal(username)); + + // if ( + // get(currentAccount, 'name') === params.username && + // (pageType === 'main' || pageType === 'ownProfile') + // ) { + // navigation.navigate(ROUTES.TABBAR.PROFILE); + // } else { + // navigation.navigate({ + // routeName: ROUTES.SCREENS.PROFILE, + // params, + // key: get(_content, 'author'), + // }); + // } } }; diff --git a/src/redux/actions/uiAction.ts b/src/redux/actions/uiAction.ts index cbf03406c..68d9568a4 100644 --- a/src/redux/actions/uiAction.ts +++ b/src/redux/actions/uiAction.ts @@ -9,6 +9,8 @@ import { SHOW_ACTION_MODAL, HIDE_ACTION_MODAL, SET_AVATAR_CACHE_STAMP, + SHOW_PROFILE_MODAL, + HIDE_PROFILE_MODAL } from '../constants/constants'; export const updateActiveBottomTab = (payload:string) => ({ @@ -35,10 +37,24 @@ export const showActionModal = (title:string, body?:string, buttons?:AlertButton type: SHOW_ACTION_MODAL, }); + + export const hideActionModal = () => ({ type: HIDE_ACTION_MODAL, }); + +export const showProfileModal = (username:string) => ({ + payload: { + profileModalUsername: username + }, + type: SHOW_PROFILE_MODAL, +}); + +export const hideProfileModal = () => ({ + type: HIDE_PROFILE_MODAL, +}); + export const setRcOffer = (payload:boolean) => ({ payload, type: RC_OFFER, diff --git a/src/redux/constants/constants.js b/src/redux/constants/constants.js index 8c5de7830..d90fef688 100644 --- a/src/redux/constants/constants.js +++ b/src/redux/constants/constants.js @@ -55,6 +55,8 @@ export const TOGGLE_ACCOUNTS_BOTTOM_SHEET = 'TOGGLE_ACCOUNTS_BOTTOM_SHEET'; export const SHOW_ACTION_MODAL = 'SHOW_ACTION_MODAL'; export const HIDE_ACTION_MODAL = 'HIDE_ACTION_MODAL'; export const SET_AVATAR_CACHE_STAMP = 'SET_AVATAR_CACHE_STAMP'; +export const SHOW_PROFILE_MODAL = 'SHOW_PROFILE_MODAL'; +export const HIDE_PROFILE_MODAL = 'HIDE_PROFILE_MODAL'; // POSTS export const SET_FEED_POSTS = 'SET_FEED_POSTS'; diff --git a/src/redux/reducers/uiReducer.ts b/src/redux/reducers/uiReducer.ts index b792ac16d..04652e1cf 100644 --- a/src/redux/reducers/uiReducer.ts +++ b/src/redux/reducers/uiReducer.ts @@ -7,6 +7,8 @@ import { SHOW_ACTION_MODAL, HIDE_ACTION_MODAL, SET_AVATAR_CACHE_STAMP, + SHOW_PROFILE_MODAL, + HIDE_PROFILE_MODAL, } from '../constants/constants'; interface UiState { @@ -17,7 +19,8 @@ interface UiState { isVisibleAccountsBottomSheet:boolean; actionModalVisible:boolean; actionModalData:any; - avatarCacheStamp:number + avatarCacheStamp:number; + profileModalUsername:string; } const initialState:UiState = { @@ -28,7 +31,8 @@ const initialState:UiState = { isVisibleAccountsBottomSheet: false, actionModalVisible: false, actionModalData: null, - avatarCacheStamp: 0 + avatarCacheStamp: 0, + profileModalUsername: '' }; export default function (state = initialState, action) { @@ -61,6 +65,20 @@ export default function (state = initialState, action) { }; } + case SHOW_PROFILE_MODAL: { + return { + ...state, + profileModalUsername: action.payload.profileModalUsername, + }; + } + + case HIDE_PROFILE_MODAL: { + return { + ...state, + profileModalUsername: '', + }; + } + case RC_OFFER: return { ...state, diff --git a/src/screens/application/screen/applicationScreen.js b/src/screens/application/screen/applicationScreen.js index db7755d72..476e18b8e 100644 --- a/src/screens/application/screen/applicationScreen.js +++ b/src/screens/application/screen/applicationScreen.js @@ -25,6 +25,7 @@ import { AccountsBottomSheet, ActionModal, ForegroundNotification, + QuickProfileModal, } from '../../../components'; // Themes (Styles) @@ -171,6 +172,7 @@ class ApplicationScreen extends Component { + ); } From 0b5126f47e277cb1976a88f16ea2122553d4ee2e Mon Sep 17 00:00:00 2001 From: noumantahir Date: Sat, 11 Sep 2021 22:51:39 +0500 Subject: [PATCH 02/16] redering basic profile data in modal --- .../children/quickProfileContent.tsx | 150 +++++++++++++++++- .../container/quickProfileModal.tsx | 79 +-------- 2 files changed, 146 insertions(+), 83 deletions(-) diff --git a/src/components/organisms/quickProfileModal/children/quickProfileContent.tsx b/src/components/organisms/quickProfileModal/children/quickProfileContent.tsx index 3adafbae3..39b7d0bbf 100644 --- a/src/components/organisms/quickProfileModal/children/quickProfileContent.tsx +++ b/src/components/organisms/quickProfileModal/children/quickProfileContent.tsx @@ -1,20 +1,154 @@ -import React from 'react' -import { View, Text, ActivityIndicator } from 'react-native' +import React, { useEffect, useState } from 'react' +import { useIntl } from 'react-intl' +import { View, Text, ActivityIndicator, Alert, Button } from 'react-native' +import FastImage from 'react-native-fast-image' +import { checkFavorite } from '../../../../providers/ecency/ecency' +import { getFollows, getRelationship, getUser } from '../../../../providers/hive/dhive' +import { getRcPower, getVotingPower } from '../../../../utils/manaBar' import styles from './quickProfileStyles' interface QuickProfileContentProps { - isLoading:boolean, - username:string + username:string, + currentAccountName:string; } export const QuickProfileContent = ({ - isLoading, - username + currentAccountName, + username, + }:QuickProfileContentProps) => { + const intl = useIntl(); + const [isLoading, setIsLoading] = useState(false); + const [user, setUser] = useState(null); + const [follows, setFollows] = useState(null); + const [isFollowing, setIsFollowing] = useState(false); + const [isMuted, setIsMuted] = useState(false); + const [isFavourite, setIsFavourite] = useState(false); + + const isOwnProfile = currentAccountName === username; + + useEffect(() => { + if(username) { + _fetchUser(); + _fetchExtraUserData(); + } else { + setUser(null); + } + }, [username]) + + + //NETWORK CALLS + const _fetchUser = async () => { + setIsLoading(true); + try { + const _user = await getUser(username, isOwnProfile); + setUser(_user) + } catch (error) { + setIsLoading(false); + } + }; + + + const _fetchExtraUserData = async () => { + try { + if (username) { + let _isFollowing; + let _isMuted; + let _isFavourite; + let follows; + + if (!isOwnProfile) { + const res = await getRelationship(currentAccountName, username); + _isFollowing = res && res.follows; + _isMuted = res && res.ignores; + _isFavourite = await checkFavorite(username); + } + + try { + follows = await getFollows(username); + } catch (err) { + follows = null; + } + + + setFollows(follows); + setIsFollowing(_isFollowing); + setIsMuted(_isMuted) + setIsFavourite(_isFavourite) + setIsLoading(false); + + } + } catch (error) { + console.warn('Failed to fetch complete profile data', error); + Alert.alert( + intl.formatMessage({ + id: 'alert.fail', + }), + error.message || error.toString(), + ); + } + }; + + + //UI CALLBACKS + + + //UI RENDERERS + if(!user){ + return + } + + //extract prop values + let _votingPower = ''; + let _resourceCredits = ''; + let _followerCount = 0; + let _followingCount = 0; + let _postCount = 0; + let _avatarUrl = ''; + let _about = ''; + + if (user) { + _votingPower = getVotingPower(user).toFixed(1); + _resourceCredits = getRcPower(user).toFixed(1); + _postCount = user.post_count || 0; + _avatarUrl = user.avatar || ''; + _about = user.about?.profile?.about || ''; + } + + if(follows){ + _followerCount = follows.follower_count || 0; + _followingCount = follows.following_count || 0 + } + return ( - Profile content goes here - {username} + + {`@${username}`} + {_about} + {_votingPower} + {_resourceCredits} + {_followerCount} + {_followingCount} + {_postCount} + {isFollowing?"following":"not following"} + {isMuted?"muted":"not muted"} + {isFavourite?"fav":"not fav"} +