Merge pull request #2062 from ecency/nt/quick-profile

Nt/quick profile
This commit is contained in:
Feruz M 2021-09-17 10:15:25 +03:00 committed by GitHub
commit 09d05f59ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 700 additions and 53 deletions

View File

@ -442,7 +442,7 @@ PODS:
- React-Core
- RNScreens (2.18.1):
- React-Core
- RNSVG (9.13.6):
- RNSVG (12.1.1):
- React
- RNVectorIcons (6.7.0):
- React
@ -761,7 +761,7 @@ SPEC CHECKSUMS:
RNOS: 6f2f9a70895bbbfbdad7196abd952e7b01d45027
RNReanimated: e03f7425cb7a38dcf1b644d680d1bfc91c3337ad
RNScreens: f7ad633b2e0190b77b6a7aab7f914fad6f198d8d
RNSVG: 8ba35cbeb385a52fd960fd28db9d7d18b4c2974f
RNSVG: 551acb6562324b1d52a4e0758f7ca0ec234e278f
RNVectorIcons: 368d6d8b8301224e5ffb6254191f4f8876c2be0d
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
SDWebImageWebPCoder: d0dac55073088d24b2ac1b191a71a8f8d0adac21

View File

@ -105,6 +105,7 @@
"react-native-modal-translucent": "^5.0.0",
"react-native-navigation-bar-color": "^1.0.0",
"react-native-os": "^1.0.1",
"react-native-progress": "^5.0.0",
"react-native-push-notification": "^7.3.1",
"react-native-qrcode-svg": "^6.0.3",
"react-native-randombytes": "^3.6.1",
@ -117,7 +118,7 @@
"react-native-scrollable-tab-view": "ecency/react-native-scrollable-tab-view",
"react-native-snap-carousel": "^3.8.0",
"react-native-splash-screen": "^3.2.0",
"react-native-svg": "^9.5.3",
"react-native-svg": "^12.1.1",
"react-native-swiper": "^1.6.0-rc.3",
"react-native-tcp": "^3.2.1",
"react-native-udp": "^2.1.0",

View File

@ -7,6 +7,7 @@ import FontAwesome from 'react-native-vector-icons/FontAwesome';
import Feather from 'react-native-vector-icons/Feather';
import AntDesign from 'react-native-vector-icons/AntDesign';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
import styles from './iconStyles';
@ -46,6 +47,8 @@ class IconView extends PureComponent {
return <Feather {...this.props} />;
case 'FontAwesome':
return <FontAwesome {...this.props} />;
case 'FontAwesome5':
return <FontAwesome5 {...this.props} />;
case 'SimpleLineIcons':
return <SimpleLineIcons {...this.props}>{children}</SimpleLineIcons>;
case 'AntDesign':

View File

@ -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,
};

View File

@ -188,6 +188,7 @@ class NotificationView extends PureComponent {
<NotificationLine
notification={item}
handleOnPressNotification={this.props.navigateToNotificationRoute}
handleOnUserPress={()=>{this.props.handleOnUserPress(item.source)}}
/>
</>
)

View File

@ -1,6 +1,6 @@
/* eslint-disable react/jsx-one-expression-per-line */
import React, { useState, useEffect } from 'react';
import { View, Text, Image, TouchableHighlight } from 'react-native';
import { View, Text, Image, TouchableHighlight, TouchableOpacity } from 'react-native';
import { useIntl } from 'react-intl';
import get from 'lodash/get';
@ -10,7 +10,7 @@ import { UserAvatar } from '../../userAvatar';
// Styles
import styles from './notificationLineStyles';
const NotificationLineView = ({ notification, handleOnPressNotification }) => {
const NotificationLineView = ({ notification, handleOnPressNotification, handleOnUserPress }) => {
const [isRead, setIsRead] = useState(notification.read);
const intl = useIntl();
let _title;
@ -58,10 +58,14 @@ const NotificationLineView = ({ notification, handleOnPressNotification }) => {
key={`${get(notification, 'id')}${_title}`}
style={[styles.notificationWrapper, !isRead && styles.isNewNotification]}
>
<TouchableOpacity onPress={handleOnUserPress}>
<UserAvatar
noAction={true}
username={notification.source}
style={[styles.avatar, !notification.avatar && styles.hasNoAvatar]}
/>
</TouchableOpacity>
<View style={styles.body}>
<View style={styles.titleWrapper}>
<Text style={styles.name}>{notification.source} </Text>

View File

@ -0,0 +1 @@
export * from './quickProfileModal';

View File

@ -0,0 +1,45 @@
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import EStyleSheet from 'react-native-extended-stylesheet';
import { IconButton } from '../../..';
import { useAppSelector } from '../../../../hooks';
import styles from './quickProfileStyles';
interface ActionPanelProps {
isFollowing:boolean,
isFavourite:boolean,
onFollowPress:()=>void,
onFavouritePress:()=>void
}
export const ActionPanel = ({isFollowing, isFavourite, onFavouritePress, onFollowPress}: ActionPanelProps) => {
const heartColor = isFavourite
? '$primaryRed'
: '$primaryDarkGray'
const followIcon = isFollowing
? 'user-check'
: 'user-plus'
return (
<View style={styles.actionPanel}>
<IconButton
iconType='FontAwesome5'
name={followIcon}
size={20}
color={EStyleSheet.value('$primaryDarkGray')}
disabled={isFollowing}
onPress={onFollowPress}
/>
<IconButton
style={{marginLeft:8}}
iconType='AntDesign'
name={'heart'}
size={20}
color={EStyleSheet.value(heartColor)}
onPress={onFavouritePress}
/>
</View>
);
};

View File

@ -0,0 +1 @@
export * from './quickProfileContent';

View File

@ -0,0 +1,61 @@
import React from 'react'
import { View, Text, TouchableOpacity } from 'react-native'
import FastImage from 'react-native-fast-image';
import styles from './quickProfileStyles';
import * as Progress from 'react-native-progress';
import EStyleSheet from 'react-native-extended-stylesheet';
import { useIntl } from 'react-intl';
interface Props {
avatarUrl:string,
username:string,
about:string,
created:{
unit:string,
value:number
},
votingPower:string,
isLoading:boolean,
onPress:()=>void
}
export const ProfileBasic = ({avatarUrl, username, about, votingPower, isLoading, created, onPress}: Props) => {
const intl = useIntl();
const progress = parseInt(votingPower || '0')/100;
let joinedString = '---'
if(created){
const timeString = `${(-created.value)} ${intl.formatMessage({id:`time.${created.unit}`})}`;
joinedString = intl.formatMessage({id:'profile.joined'}, {time:timeString})
}
return (
<TouchableOpacity onPress={onPress} >
<View style={styles.container}>
<View>
<FastImage
source={{uri:avatarUrl}}
resizeMode='cover'
style={styles.image}
/>
<View style={styles.progressCircle}>
<Progress.Circle
size={144}
indeterminate={isLoading}
progress={progress}
borderColor='gray'
borderWidth={0}
thickness={8}
strokeCap='round'
color={EStyleSheet.value('$primaryBlue')}
/>
</View>
</View>
<Text style={styles.title}>{`@${username}`}</Text>
<Text style={styles.bodyText} numberOfLines={2} >{about}</Text>
<Text style={styles.bodyText}>{joinedString}</Text>
</View>
</TouchableOpacity>
)
}

View File

@ -0,0 +1,41 @@
import React from 'react'
import { View, Text } from 'react-native'
import styles from './quickProfileStyles'
import * as Animated from 'react-native-animatable'
export interface StatsData {
label:string,
value:number|string,
suffix?:string
}
interface Props {
data:StatsData[],
horizontalMargin?:number
}
export const ProfileStats = ({data, horizontalMargin}: Props) => {
return (
<View style={{flexDirection:'row', justifyContent:'space-around', marginTop:40, marginHorizontal:horizontalMargin }}>
{data.map((item)=><StatItem label={item.label} value={item.value && (item.value + (item.suffix || ''))}/>)}
</View>
)
}
const StatItem = (props:{label:string, value:number|string}) => (
<View style={{alignItems:'center', flex:1}}>
{!!props.value ? (
<Animated.Text animation='bounceIn' style={styles.statValue}>{props.value}</Animated.Text>
):(
<Text style={styles.statValue}>{'--'}</Text>
)}
<Text style={styles.statLabel}>{props.label}</Text>
</View>
)

View File

@ -0,0 +1,273 @@
import React, { useEffect, useState } from 'react'
import { useIntl } from 'react-intl'
import { View, Alert } from 'react-native'
import { ProfileStats, StatsData } from './profileStats'
import { MainButton } from '../../..'
import { addFavorite, checkFavorite, deleteFavorite } from '../../../../providers/ecency/ecency'
import { followUser, getFollows, getRelationship, getUser } from '../../../../providers/hive/dhive'
import { getRcPower, getVotingPower } from '../../../../utils/manaBar'
import styles from './quickProfileStyles'
import { ProfileBasic } from './profileBasic'
import { parseReputation } from '../../../../utils/user'
import { default as ROUTES } from '../../../../constants/routeNames';
import { ActionPanel } from './actionPanel'
import { getTimeFromNowNative } from '../../../../utils/time'
import { useAppDispatch, useAppSelector } from '../../../../hooks'
import { toastNotification } from '../../../../redux/actions/uiAction'
import Bugsnag from '@bugsnag/react-native'
interface QuickProfileContentProps {
username:string,
navigation:any;
onClose:()=>void;
}
export const QuickProfileContent = ({
username,
navigation,
onClose
}:QuickProfileContentProps) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const currentAccount = useAppSelector((state)=>state.account.currentAccount);
const pinCode = useAppSelector((state)=>state.application.pin);
const isLoggedIn = useAppSelector((state)=>state.application.isLoggedIn);
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 = currentAccount && currentAccount.name === username;
const currentAccountName = currentAccount ? currentAccount.name : null;
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(),
);
setIsLoading(false);
}
};
const _onFollowPress = async () => {
try{
const follower = currentAccountName
const following = username;
setIsLoading(true);
await followUser(currentAccount, pinCode, {
follower,
following,
})
setIsLoading(false);
setIsFollowing(true)
dispatch(
toastNotification(
intl.formatMessage({
id: isFollowing ? 'alert.success_unfollow' : 'alert.success_follow',
}),
),
);
}
catch(err){
setIsLoading(false);
console.warn("Failed to follow user", err)
Bugsnag.notify(err);
Alert.alert(intl.formatMessage({id:'alert.fail'}), err.message)
}
}
const _onFavouritePress = async () => {
try{
setIsLoading(true);
let favoriteAction;
if (isFavourite) {
favoriteAction = deleteFavorite;
} else {
favoriteAction = addFavorite;
}
await favoriteAction(username)
dispatch(
toastNotification(
intl.formatMessage({
id: isFavourite ? 'alert.success_unfavorite' : 'alert.success_favorite',
}),
),
);
setIsFavourite(!isFavourite);
setIsLoading(false);
}
catch(error){
console.warn('Failed to perform favorite action');
setIsLoading(false);
Alert.alert(
intl.formatMessage({
id: 'alert.fail',
}),
error.message || error.toString(),
);
}
}
//UI CALLBACKS
const _openFullProfile = () => {
let params = {
username,
reputation: user ? user.reputation : null
};
if (isOwnProfile) {
navigation.navigate(ROUTES.TABBAR.PROFILE);
} else {
navigation.navigate({
routeName: ROUTES.SCREENS.PROFILE,
params,
key: username,
});
}
if(onClose){
onClose();
}
}
//extract prop values
let _votingPower = '';
let _resourceCredits = '';
let _followerCount = 0;
let _followingCount = 0;
let _postCount = 0;
let _avatarUrl = '';
let _about = '';
let _reputation = 0;
let _createdData = null;
if (user && follows) {
_votingPower = getVotingPower(user).toFixed(1);
_resourceCredits = getRcPower(user).toFixed(0);
_postCount = user.post_count || 0;
_avatarUrl = user.avatar || '';
_about = user.about?.profile?.about || '';
_reputation = parseReputation(user.reputation);
_createdData = getTimeFromNowNative(user.created)
if(follows){
_followerCount = follows.follower_count || 0;
_followingCount = follows.following_count || 0
}
}
const statsData1 = [
{label:'Follower', value:_followerCount},
{label:'Following', value:_followingCount},
{label:'Posts', value:_postCount},
] as StatsData[]
const statsData2 = [
{label:'Resource Credits', value:_resourceCredits, suffix:'%'},
{label:'Reputation', value:_reputation},
] as StatsData[]
return (
<View style={styles.modalStyle}>
<ProfileBasic
username={username}
about={_about}
avatarUrl={_avatarUrl}
created={_createdData}
votingPower={_votingPower}
isLoading={isLoading}
onPress={_openFullProfile}
/>
<ProfileStats
data={statsData1}
/>
<ProfileStats
horizontalMargin={16}
data={statsData2}
/>
<MainButton
style={styles.button}
text='VIEW FULL PROFILE'
onPress={_openFullProfile}
/>
{isLoggedIn && (
<ActionPanel
isFollowing={isFollowing}
isFavourite={isFavourite}
onFavouritePress={_onFavouritePress}
onFollowPress={_onFollowPress}
/>
)}
</View>
)
};

View File

@ -0,0 +1,109 @@
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,
marginHorizontal:24,
},
sheetContent: {
backgroundColor: '$primaryBackgroundColor',
position:'absolute',
bottom:0,
left:0,
right:0,
zIndex:999
},
container:{
alignItems:'center',
marginHorizontal:16
} as ViewStyle,
image:{
width:128,
height:128,
borderRadius:64,
backgroundColor: '$primaryGray'
} as ImageStyle,
textContainer:{
marginTop:32,
marginBottom:44,
} as ViewStyle,
title: {
color: '$primaryBlack',
alignSelf: 'center',
textAlign: 'center',
fontSize: 18,
fontWeight: 'bold',
marginTop:32,
} as TextStyle,
statValue: {
fontFamily:'$editorFont',
color: '$primaryBlack',
alignSelf: 'center',
textAlign: 'center',
fontSize: 34,
fontWeight: 'normal',
} as TextStyle,
statLabel: {
color: '$primaryBlack',
alignSelf: 'center',
textAlign: 'center',
fontSize: 16,
fontWeight: 'bold',
} as TextStyle,
bodyText: {
color: '$primaryBlack',
alignSelf: 'center',
textAlign: 'center',
fontSize: 18,
fontWeight: '500',
marginTop:6,
} as TextStyle,
btnText:{
color:'$pureWhite'
} as TextStyle,
button:{
marginTop: 40,
backgroundColor:'$primaryBlue',
paddingHorizontal:44,
paddingVertical:16,
borderRadius:32,
justifyContent:'center',
alignItems:'center'
} as ViewStyle,
actionPanel:{
position: 'absolute',
right:0,
top:0,
flexDirection:'row',
alignItems:'center',
} as ViewStyle,
progressCircle:{
position:'absolute',
top:0,
bottom:0,
left:0,
right:0,
alignItems:'center',
justifyContent:'center'
} as ViewStyle
})

View File

@ -0,0 +1,54 @@
import React, { useEffect, useRef } from 'react';
import { 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';
export interface ActionModalData {
navigation:any
}
export const QuickProfileModal = ({navigation}) => {
const sheetModalRef = useRef<ActionSheet>();
const dispatch = useAppDispatch();
const profileModalUsername = useAppSelector((state)=>state.ui.profileModalUsername);
useEffect(() => {
if(profileModalUsername){
sheetModalRef.current.show();
}else {
sheetModalRef.current.hide();
}
}, [profileModalUsername])
const _onClose = () => {
dispatch(hideProfileModal());
}
return (
<ActionSheet
ref={sheetModalRef}
gestureEnabled={true}
containerStyle={styles.sheetContent}
onClose={_onClose}
indicatorColor={EStyleSheet.value('$primaryWhiteLightBackground')}>
<QuickProfileContent
navigation={navigation}
username={profileModalUsername}
onClose={_onClose}
/>
</ActionSheet>
);
};

View File

@ -0,0 +1 @@
export * from './container/quickProfileModal';

View File

@ -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,8 @@ const PostCardContainer = ({
const _handleOnUserPress = (username) => {
if (_content) {
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'),
});
}
username = username || get(_content, 'author');
dispatch(showProfileModal(username));
}
};

View File

@ -1,5 +1,6 @@
import React, { PureComponent } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
import { withNavigation } from 'react-navigation';
import { injectIntl } from 'react-intl';
@ -12,6 +13,7 @@ import styles from './postHeaderDescriptionStyles';
import { default as ROUTES } from '../../../../constants/routeNames';
import { IconButton } from '../../..';
import { showProfileModal } from '../../../../redux/actions/uiAction';
// Constants
const DEFAULT_IMAGE = require('../../../../assets/ecency.png');
@ -21,19 +23,12 @@ class PostHeaderDescription extends PureComponent {
// Component Functions
_handleOnUserPress = (username) => {
const { navigation, profileOnPress, reputation } = this.props;
const { profileOnPress, dispatch } = this.props;
if (profileOnPress) {
profileOnPress(username);
} else {
navigation.navigate({
routeName: ROUTES.SCREENS.PROFILE,
params: {
username,
reputation,
},
key: username,
});
dispatch(showProfileModal(username));
}
};
@ -181,4 +176,6 @@ class PostHeaderDescription extends PureComponent {
}
}
export default withNavigation(injectIntl(PostHeaderDescription));
const mapStateToProps = () => ({});
export default connect(mapStateToProps)(withNavigation(injectIntl(PostHeaderDescription)));

View File

@ -174,6 +174,7 @@
"day": "day",
"hive_dollars": "Hive Dollars",
"savings": "Savings",
"joined": "Joined {time} ago",
"edit": {
"display_name": "Display Name",
"about": "About",
@ -672,5 +673,14 @@
"line3_heading":"Join Ecency communities!",
"line3_body":"Build community you own, get rewarded and reward others.",
"get_started":"Get started!"
},
"time":{
"second":"seconds",
"minute":"minutes",
"hour":"hours",
"week":"weeks",
"day":"days",
"month":"months",
"year":"years"
}
}

View File

@ -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,

View File

@ -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';

View File

@ -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,

View File

@ -87,6 +87,7 @@ import {
} from '../../../redux/actions/applicationActions';
import {
hideActionModal,
hideProfileModal,
setAvatarCacheStamp,
setRcOffer,
showActionModal,
@ -761,6 +762,7 @@ class ApplicationContainer extends Component {
//reset certain properties
dispatch(hideActionModal());
dispatch(hideProfileModal());
dispatch(toastNotification(''));
dispatch(resetLocalVoteMap());
dispatch(setRcOffer(false));

View File

@ -25,6 +25,7 @@ import {
AccountsBottomSheet,
ActionModal,
ForegroundNotification,
QuickProfileModal,
} from '../../../components';
// Themes (Styles)
@ -113,6 +114,7 @@ class ApplicationScreen extends Component {
toastNotification,
isReady,
foregroundNotificationData,
navigation,
} = this.props;
const { isShowToastNotification, showWelcomeModal } = this.state;
const barStyle = isDarkTheme ? 'light-content' : 'dark-content';
@ -171,6 +173,7 @@ class ApplicationScreen extends Component {
<ForegroundNotification remoteMessage={foregroundNotificationData} />
<AccountsBottomSheet />
<ActionModal />
<QuickProfileModal navigation={{ navigate }} />
</View>
);
}

View File

@ -15,6 +15,7 @@ import ROUTES from '../../../constants/routeNames';
// Components
import NotificationScreen from '../screen/notificationScreen';
import { showProfileModal } from '../../../redux/actions/uiAction';
class NotificationContainer extends Component {
constructor(props) {
@ -118,6 +119,11 @@ class NotificationContainer extends Component {
}
};
_handleOnUserPress = (username) => {
const { dispatch } = this.props;
dispatch(showProfileModal(username));
};
_readAllNotification = () => {
const { dispatch, intl, isConnected } = this.props;
const { notifications } = this.state;
@ -174,6 +180,7 @@ class NotificationContainer extends Component {
getActivities={this._getActivities}
notifications={notifications}
navigateToNotificationRoute={this._navigateToNotificationRoute}
handleOnUserPress={this._handleOnUserPress}
readAllNotification={this._readAllNotification}
handleLoginPress={this._handleOnPressLogin}
isNotificationRefreshing={isRefreshing}

View File

@ -16,6 +16,7 @@ const NotificationScreen = ({
getActivities,
intl,
navigateToNotificationRoute,
handleOnUserPress,
readAllNotification,
isNotificationRefreshing,
changeSelectedFilter,
@ -42,6 +43,7 @@ const NotificationScreen = ({
getActivities={getActivities}
notifications={notifications}
navigateToNotificationRoute={navigateToNotificationRoute}
handleOnUserPress={handleOnUserPress}
readAllNotification={readAllNotification}
isNotificationRefreshing={isNotificationRefreshing}
changeSelectedFilter={changeSelectedFilter}

View File

@ -3513,7 +3513,7 @@ css-mediaquery@^0.1.2:
resolved "https://registry.yarnpkg.com/css-mediaquery/-/css-mediaquery-0.1.2.tgz#6a2c37344928618631c54bd33cedd301da18bea0"
integrity sha1-aiw3NEkoYYYxxUvTPO3TAdoYvqA=
css-select@^2.0.2:
css-select@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef"
integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==
@ -3532,10 +3532,10 @@ css-to-react-native@^3.0.0:
css-color-keywords "^1.0.0"
postcss-value-parser "^4.0.2"
css-tree@^1.0.0-alpha.37:
version "1.1.2"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.2.tgz#9ae393b5dafd7dae8a622475caec78d3d8fbd7b5"
integrity sha512-wCoWush5Aeo48GLhfHPbmvZs59Z+M7k5+B1xDnXbdWNcEF423DoFdqSWE0PM5aNk5nI5cp1q7ms36zGApY/sKQ==
css-tree@^1.0.0-alpha.39:
version "1.1.3"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==
dependencies:
mdn-data "2.0.14"
source-map "^0.6.1"
@ -8877,6 +8877,13 @@ react-native-os@^1.0.1:
resolved "https://registry.yarnpkg.com/react-native-os/-/react-native-os-1.2.6.tgz#1bb16d78ccad1143972183a04f443cf1af9fbefa"
integrity sha512-OlT+xQAcvkcnf7imgXiu+myMkqDt4xw2bP5SlVo19hEn5XHBkPMLX7dk3sSGxxncH/ToMDsf1KLyrPabNVtadA==
react-native-progress@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/react-native-progress/-/react-native-progress-5.0.0.tgz#f5ac6ceaeee27f184c660b00f29419e82a9d0ab0"
integrity sha512-KjnGIt3r9i5Kn2biOD9fXLJocf0bwxPRxOyAgXEnZTJQU2O+HyzgGFRCbM5h3izm9kKIkSc1txh8aGmMafCD9A==
dependencies:
prop-types "^15.7.2"
react-native-push-notification@^7.3.1:
version "7.3.1"
resolved "https://registry.yarnpkg.com/react-native-push-notification/-/react-native-push-notification-7.3.1.tgz#1495feacd25169b998446dcf7b448a197ae5dca0"
@ -8975,13 +8982,13 @@ react-native-splash-screen@^3.2.0:
resolved "https://registry.yarnpkg.com/react-native-splash-screen/-/react-native-splash-screen-3.2.0.tgz#d47ec8557b1ba988ee3ea98d01463081b60fff45"
integrity sha512-Ls9qiNZzW/OLFoI25wfjjAcrf2DZ975hn2vr6U9gyuxi2nooVbzQeFoQS5vQcbCt9QX5NY8ASEEAtlLdIa6KVg==
react-native-svg@^9.5.3:
version "9.13.6"
resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-9.13.6.tgz#5365fba2bc460054b90851e71f2a71006a5d373f"
integrity sha512-vjjuJhEhQCwWjqsgWyGy6/C/LIBM2REDxB40FU1PMhi8T3zQUwUHnA6M15pJKlQG8vaZyA+QnLyIVhjtujRgig==
react-native-svg@^12.1.1:
version "12.1.1"
resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-12.1.1.tgz#5f292410b8bcc07bbc52b2da7ceb22caf5bcaaee"
integrity sha512-NIAJ8jCnXGCqGWXkkJ1GTzO4a3Md5at5sagYV8Vh4MXYnL4z5Rh428Wahjhh+LIjx40EE5xM5YtwyJBqOIba2Q==
dependencies:
css-select "^2.0.2"
css-tree "^1.0.0-alpha.37"
css-select "^2.1.0"
css-tree "^1.0.0-alpha.39"
react-native-swiper@^1.6.0-rc.3:
version "1.6.0"