Skip to content

Commit

Permalink
Merge pull request #2944 from studio-504/create-popup-component
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander Mechler authored May 20, 2021
2 parents 984360d + 30e57b9 commit 3628780
Show file tree
Hide file tree
Showing 11 changed files with 255 additions and 33 deletions.
8 changes: 8 additions & 0 deletions assets/images/exclamation.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 35 additions & 33 deletions src/components/Icon/Expensicons.js
Original file line number Diff line number Diff line change
@@ -1,65 +1,74 @@
import Android from '../../../assets/images/android.svg';
import Apple from '../../../assets/images/apple.svg';
import ArrowRight from '../../../assets/images/arrow-right.svg';
import BackArrow from '../../../assets/images/back-left.svg';
import Bug from '../../../assets/images/bug.svg';
import Camera from '../../../assets/images/camera.svg';
import ChatBubble from '../../../assets/images/chatbubble.svg';
import Checkmark from '../../../assets/images/checkmark.svg';
import Clipboard from '../../../assets/images/clipboard.svg';
import Close from '../../../assets/images/close.svg';
import DownArrow from '../../../assets/images/down.svg';
import Download from '../../../assets/images/download.svg';
import Emoji from '../../../assets/images/emoji.svg';
import Exclamation from '../../../assets/images/exclamation.svg';
import Eye from '../../../assets/images/eye.svg';
import Gallery from '../../../assets/images/gallery.svg';
import Gear from '../../../assets/images/gear.svg';
import Info from '../../../assets/images/info.svg';
import Link from '../../../assets/images/link.svg';
import LinkCopy from '../../../assets/images/link-copy.svg';
import Lock from '../../../assets/images/lock.svg';
import MagnifyingGlass from '../../../assets/images/magnifyingglass.svg';
import Mail from '../../../assets/images/mail.svg';
import MoneyBag from '../../../assets/images/money-bag.svg';
import MoneyCircle from '../../../assets/images/money-circle.svg';
import Monitor from '../../../assets/images/monitor.svg';
import NewWindow from '../../../assets/images/new-window.svg';
import Offline from '../../../assets/images/offline.svg';
import Paperclip from '../../../assets/images/paperclip.svg';
import Pencil from '../../../assets/images/pencil.svg';
import Phone from '../../../assets/images/phone.svg';
import Pin from '../../../assets/images/pin.svg';
import PinCircle from '../../../assets/images/pin-circle.svg';
import Plus from '../../../assets/images/plus.svg';
import Profile from '../../../assets/images/profile.svg';
import Receipt from '../../../assets/images/receipt.svg';
import Send from '../../../assets/images/send.svg';
import SignOut from '../../../assets/images/sign-out.svg';
import Trashcan from '../../../assets/images/trashcan.svg';
import Users from '../../../assets/images/users.svg';
import Checkmark from '../../../assets/images/checkmark.svg';
import Receipt from '../../../assets/images/receipt.svg';
import MoneyCircle from '../../../assets/images/money-circle.svg';
import Download from '../../../assets/images/download.svg';
import DownArrow from '../../../assets/images/down.svg';
import Profile from '../../../assets/images/profile.svg';
import Gear from '../../../assets/images/gear.svg';
import Wallet from '../../../assets/images/wallet.svg';
import Lock from '../../../assets/images/lock.svg';
import ArrowRight from '../../../assets/images/arrow-right.svg';
import Emoji from '../../../assets/images/emoji.svg';
import Upload from '../../../assets/images/upload.svg';
import Camera from '../../../assets/images/camera.svg';
import Gallery from '../../../assets/images/gallery.svg';
import Offline from '../../../assets/images/offline.svg';
import SignOut from '../../../assets/images/sign-out.svg';
import Info from '../../../assets/images/info.svg';
import Link from '../../../assets/images/link.svg';
import Eye from '../../../assets/images/eye.svg';
import Android from '../../../assets/images/android.svg';
import Apple from '../../../assets/images/apple.svg';
import Bug from '../../../assets/images/bug.svg';
import MoneyBag from '../../../assets/images/money-bag.svg';
import Monitor from '../../../assets/images/monitor.svg';
import NewWindow from '../../../assets/images/new-window.svg';
import Wallet from '../../../assets/images/wallet.svg';

export {
Android,
Apple,
ArrowRight,
BackArrow,
DownArrow,
Bug,
Camera,
ChatBubble,
Checkmark,
Clipboard,
Close,
DownArrow,
Download,
Emoji,
Exclamation,
Eye,
Gallery,
Gear,
Info,
Link,
LinkCopy,
Lock,
MagnifyingGlass,
Mail,
MoneyBag,
MoneyCircle,
Monitor,
NewWindow,
Offline,
Paperclip,
Pencil,
Expand All @@ -70,16 +79,9 @@ export {
Profile,
Receipt,
Send,
SignOut,
Trashcan,
Upload,
Users,
Wallet,
SignOut,
Info,
Link,
Eye,
Bug,
MoneyBag,
Monitor,
NewWindow,
};
1 change: 1 addition & 0 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export default {
emailAddress: 'Email Address',
setMyTimezoneAutomatically: 'Set my timezone automatically',
timezone: 'Timezone',
growlMessageOnSave: 'Your profile was successfully saved',
},
addSecondaryLoginPage: {
addPhoneNumber: 'Add Phone Number',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {Animated} from 'react-native';
import PropTypes from 'prop-types';
import {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';

const propTypes = {
/** GrowlNotification content */
children: PropTypes.node.isRequired,

/** GrowlNotification Y postion, required to show or hide with fling animation */
translateY: PropTypes.instanceOf(Animated.Value).isRequired,

...windowDimensionsPropTypes,
};

export default propTypes;
22 changes: 22 additions & 0 deletions src/libs/GrowlNotification/GrowlNotificationContainer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import {Animated} from 'react-native';
import styles from '../../../styles/styles';
import withWindowDimensions from '../../../components/withWindowDimensions';
import propTypes from './GrowlNotificationContainerPropTypes';

const GrowlNotificationContainer = ({children, translateY, isSmallScreenWidth}) => (
<Animated.View
style={[
styles.growlNotificationContainer,
styles.growlNotificationDesktopContainer,
styles.growlNotificationTranslateY(translateY),
isSmallScreenWidth && styles.mwn,
]}
>
{children}
</Animated.View>
);

GrowlNotificationContainer.propTypes = propTypes;

export default withWindowDimensions(GrowlNotificationContainer);
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import {Animated} from 'react-native';
import styles from '../../../styles/styles';
import propTypes from './GrowlNotificationContainerPropTypes';

const GrowlNotificationContainer = ({children, translateY}) => (
<Animated.View
style={[
styles.growlNotificationContainer,
styles.growlNotificationTranslateY(translateY),
]}
>
{children}
</Animated.View>
);

GrowlNotificationContainer.propTypes = propTypes;

export default GrowlNotificationContainer;
104 changes: 104 additions & 0 deletions src/libs/GrowlNotification/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React, {Component} from 'react';
import {
Text, View, Animated,
} from 'react-native';
import {
Directions, FlingGestureHandler, State, TouchableWithoutFeedback,
} from 'react-native-gesture-handler';
import colors from '../../styles/colors';
import Icon from '../../components/Icon';
import {Checkmark, Exclamation} from '../../components/Icon/Expensicons';
import styles from '../../styles/styles';
import GrowlNotificationContainer from './GrowlNotificationContainer';

const types = {
success: {
icon: Checkmark,
iconColor: colors.green,
},
error: {
icon: Exclamation,
iconColor: colors.red,
},
warning: {
icon: Exclamation,
iconColor: colors.yellow,
},
};

const INACTIVE_POSITION_Y = -255;

class GrowlNotification extends Component {
constructor() {
super();

this.state = {
bodyText: '',
type: 'success',
translateY: new Animated.Value(INACTIVE_POSITION_Y),
};

this.show = this.show.bind(this);
this.fling = this.fling.bind(this);
}

/**
* Show the growl notification
*
* @param {String} bodyText
* @param {String} type
* @param {Number} duration - 2000
*/
show(bodyText, type, duration = 2000) {
this.setState({
bodyText,
type,
}, () => {
this.fling(0);
setTimeout(() => {
this.fling(INACTIVE_POSITION_Y);
}, duration);
});
}

/**
* Animate growl notification
*
* @param {Number} val
*/
fling(val = INACTIVE_POSITION_Y) {
Animated.spring(this.state.translateY, {
toValue: val,
duration: 80,
useNativeDriver: true,
}).start();
}

render() {
return (
<FlingGestureHandler
direction={Directions.UP}
onHandlerStateChange={({nativeEvent}) => {
if (nativeEvent.state === State.ACTIVE) {
this.fling(INACTIVE_POSITION_Y);
}
}}
>
<View style={styles.growlNotificationWrapper}>
<GrowlNotificationContainer translateY={this.state.translateY}>
<TouchableWithoutFeedback onPress={this.fling}>
<View style={styles.growlNotificationBox}>
<Text style={styles.growlNotificationText}>
{this.state.bodyText}
</Text>
<Icon src={types[this.state.type].icon} fill={types[this.state.type].iconColor} />
</View>
</TouchableWithoutFeedback>
</GrowlNotificationContainer>
</View>
</FlingGestureHandler>
);
}
}

export default GrowlNotification;
5 changes: 5 additions & 0 deletions src/pages/settings/Profile/ProfilePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import CreateMenu from '../../../components/CreateMenu';
import Picker from '../../../components/Picker';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import compose from '../../../libs/compose';
import GrowlNotification from '../../../libs/GrowlNotification';
import Button from '../../../components/Button';

const propTypes = {
Expand Down Expand Up @@ -130,6 +131,7 @@ class ProfilePage extends Component {
this.setAutomaticTimezone = this.setAutomaticTimezone.bind(this);
this.getLogins = this.getLogins.bind(this);
this.createMenuItems = this.createMenuItems.bind(this);
this.growlNotification = undefined;
}

componentDidUpdate(prevProps) {
Expand Down Expand Up @@ -208,6 +210,8 @@ class ProfilePage extends Component {
selected: selectedTimezone,
},
});

this.growlNotification.show(this.props.translate('profilePage.growlMessageOnSave'), 'success', 3000);
}

/**
Expand Down Expand Up @@ -257,6 +261,7 @@ class ProfilePage extends Component {

return (
<ScreenWrapper>
<GrowlNotification ref={el => this.growlNotification = el} />
<HeaderWithCloseButton
title={this.props.translate('common.profile')}
shouldShowBackButton
Expand Down
1 change: 1 addition & 0 deletions src/styles/colors.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ export default {
green: '#03d47c',
greenHover: '#03c775',
red: '#fc3826',
yellow: '#fed607',
transparent: 'transparent',
};
41 changes: 41 additions & 0 deletions src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -1411,6 +1411,47 @@ const styles = {
transform: 'translateX(-100%)',
},

growlNotificationWrapper: {
zIndex: 2,
},

growlNotificationContainer: {
flex: 1,
justifyContent: 'flex-start',
position: 'absolute',
width: '100%',
...spacing.ph5,
},

growlNotificationDesktopContainer: {
maxWidth: '380px',
top: '20px',
right: 0,
position: 'fixed',
},

growlNotificationTranslateY: y => ({
transform: [{translateY: y}],
}),

growlNotificationBox: {
backgroundColor: colors.dark,
borderRadius: variables.componentBorderRadiusNormal,
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-between',
shadowColor: '#000',
...spacing.p5,
},

growlNotificationText: {
fontSize: variables.fontSizeNormal,
fontFamily: fontFamily.GTA,
width: '90%',
lineHeight: variables.fontSizeNormalHeight,
color: themeColors.textReversed,
},

blockquote: {
borderLeftColor: themeColors.border,
borderLeftWidth: 4,
Expand Down
4 changes: 4 additions & 0 deletions src/styles/utilities/sizing.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ export default {
w50: {
width: '50%',
},

mwn: {
maxWidth: 'auto',
},
};

0 comments on commit 3628780

Please sign in to comment.