Skip to content

Commit

Permalink
Update WhatsNewModal to parse template
Browse files Browse the repository at this point in the history
  • Loading branch information
Cal-L committed Jun 8, 2022
1 parent 872a2dc commit 88d62e0
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 123 deletions.
260 changes: 161 additions & 99 deletions app/components/UI/WhatsNewModal/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {
Text,
TouchableOpacity,
ScrollView,
TouchableWithoutFeedback,
Image,
InteractionManager,
TouchableWithoutFeedback,
} from 'react-native';
import { useNavigationState } from '@react-navigation/native';
import ActionModal from '../ActionModal';
Expand All @@ -23,37 +23,62 @@ import {
WHATS_NEW_APP_VERSION_SEEN,
} from '../../../constants/storage';
import compareVersions from 'compare-versions';
import scaling from '../../../util/scaling';
import PropTypes from 'prop-types';
import { findRouteNameFromNavigatorState } from '../../../util/general';
import StyledButton from '../StyledButton';
import { useAppThemeFromContext, mockTheme } from '../../../util/theme';
const modalMargin = 24;
const modalPadding = 24;
const screenWidth = Device.getDeviceWidth();
const screenHeight = Device.getDeviceHeight();
const slideItemWidth = screenWidth - modalMargin * 2;
const maxSlideItemHeight = screenHeight - 200;
const slideImageWidth = slideItemWidth - modalPadding * 2;
const imageAspectRatio = 128 / 264;
const slideImageHeight = slideImageWidth * imageAspectRatio;

const createStyles = (colors) =>
StyleSheet.create({
wrapper: {
marginTop: 24,
maxHeight: Device.getDeviceHeight() - 200,
flex: 1,
overflow: 'hidden',
},
button: {
slideContent: {
maxHeight: maxSlideItemHeight,
},
slideItemContainer: {
flex: 1,
width: slideItemWidth,
paddingHorizontal: modalPadding,
paddingBottom: 16,
},
progessContainer: {
flexDirection: 'row',
alignSelf: 'center',
marginTop: 16,
borderColor: colors.primary.default,
borderWidth: 1,
borderRadius: 50,
padding: 12,
paddingHorizontal: 34,
marginBottom: 8,
},
buttonText: {
color: colors.primary.default,
textAlign: 'center',
...fontStyles.normal,
slideCircle: {
width: 8,
height: 8,
borderRadius: 8 / 2,
backgroundColor: colors.icon.default,
opacity: 0.4,
marginHorizontal: 8,
},
slideSolidCircle: {
opacity: 1,
},
button: {
marginTop: 8,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
marginBottom: 24,
paddingHorizontal: 24,
marginBottom: 20,
paddingHorizontal: modalPadding,
},
headerCenterAux: {
flex: 1,
Expand All @@ -67,47 +92,38 @@ const createStyles = (colors) =>
fontSize: 18,
color: colors.text.default,
},
newFeatureImageContainer: {
slideImageContainer: {
flexDirection: 'row',
flex: 1,
borderRadius: 10,
marginBottom: 24,
},
newFeatureImage: {
slideImage: {
flex: 1,
borderRadius: 10,
width: scaling.scale(280, { baseModel: 1 }),
height: scaling.scale(128, { baseModel: 1 }),
width: slideImageWidth,
height: slideImageHeight,
},
newFeatureTitle: {
slideTitle: {
...fontStyles.bold,
fontSize: 16,
marginBottom: 12,
textAlign: 'center',
color: colors.text.default,
},
newFeatureText: {
slideDescription: {
...fontStyles.normal,
fontSize: 14,
lineHeight: 20,
textAlign: 'center',
color: colors.text.default,
marginBottom: 12,
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'center',
},
featureContainer: {
marginBottom: 25,
paddingHorizontal: 24,
marginBottom: 24,
},
});

const WhatsNewModal = (props) => {
const [featuresToShow, setFeaturesToShow] = useState(null);
const [show, setShow] = useState(false);
const routes = useNavigationState((state) => state.routes);
const slideIds = [0, 1];
const [currentSlide, setCurrentSlide] = useState(slideIds[0]);
const { colors } = useAppThemeFromContext() || mockTheme;
const styles = createStyles(colors);

Expand All @@ -121,44 +137,39 @@ const WhatsNewModal = (props) => {
const lastAppVersion = await AsyncStorage.getItem(LAST_APP_VERSION);
const isUpdate = !!lastAppVersion && currentAppVersion !== lastAppVersion;

let showFeatures = [];

whatsNew.forEach((feature) => {
const seen =
!!whatsNewAppVersionSeen &&
compareVersions.compare(
whatsNewAppVersionSeen,
feature.minAppVersion,
'>=',
);
const seen =
!!whatsNewAppVersionSeen &&
compareVersions.compare(
whatsNewAppVersionSeen,
whatsNew.minAppVersion,
'>=',
);

if (seen) return;
if (seen) return;

if (feature.onlyUpdates) {
const updatingCorrect = feature.onlyUpdates && isUpdate;
if (whatsNew.onlyUpdates) {
const updatingCorrect = whatsNew.onlyUpdates && isUpdate;

if (!updatingCorrect) return;
if (!updatingCorrect) return;

const lastVersionCorrect = compareVersions.compare(
lastAppVersion,
feature.maxLastAppVersion,
'<',
);
const lastVersionCorrect = compareVersions.compare(
lastAppVersion,
whatsNew.maxLastAppVersion,
'<',
);

if (!lastVersionCorrect) return;
}
if (!lastVersionCorrect) return;
}

const versionCorrect = compareVersions.compare(
currentAppVersion,
feature.minAppVersion,
'>=',
);
const versionCorrect = compareVersions.compare(
currentAppVersion,
whatsNew.minAppVersion,
'>=',
);

if (!versionCorrect) return;
if (!versionCorrect) return;

showFeatures = [...showFeatures, ...feature.features];
});
if (showFeatures.length) setFeaturesToShow(showFeatures);
if (whatsNew.slides.length) setFeaturesToShow(true);
};
shouldShow();
}, []);
Expand All @@ -169,13 +180,13 @@ const WhatsNewModal = (props) => {
await AsyncStorage.setItem(WHATS_NEW_APP_VERSION_SEEN, version);
};

const callButton = (feature) => {
const callButton = (onPress) => {
closeModal();
feature.buttonPress && feature.buttonPress(props);
onPress(props);
};

useEffect(() => {
if (props.enabled && !!featuresToShow) {
if (props.enabled && featuresToShow) {
const route = findRouteNameFromNavigatorState(routes);
if (route === 'WalletView') {
InteractionManager.runAfterInteractions(() => {
Expand All @@ -187,6 +198,66 @@ const WhatsNewModal = (props) => {
}
}, [featuresToShow, props.enabled, routes]);

const renderSlideElement = (elementInfo) => {
switch (elementInfo.type) {
case 'title':
return (element = (
<Text style={styles.slideTitle}>{elementInfo.title}</Text>
));
case 'description':
return (element = (
<Text style={styles.slideDescription}>{elementInfo.description}</Text>
));
case 'image':
return (
<View style={styles.slideImageContainer}>
<Image
source={elementInfo.image}
style={styles.slideImage}
resizeMode={'stretch'}
/>
</View>
);
case 'button':
return (
<View style={styles.button}>
<StyledButton
type={elementInfo.buttonType}
onPress={() => callButton(elementInfo.onPress)}
>
{elementInfo.buttonText}
</StyledButton>
</View>
);
}
return null;
};

const renderSlide = (slideInfo, index) => {
const key = `slide-info-${index}`;
return (
<ScrollView key={key} style={styles.slideItemContainer}>
<TouchableWithoutFeedback>
<View>
{slideInfo.map((elementInfo, elIndex) => {
const elKey = `${key}-${elIndex}`;
return <View key={elKey}>{renderSlideElement(elementInfo)}</View>;
})}
</View>
</TouchableWithoutFeedback>
</ScrollView>
);
};

const onScrollEnd = (e) => {
const xOffset = e.nativeEvent.contentOffset.x;
const slideIndex = Math.round(xOffset / screenWidth);
if (currentSlide === slideIndex) {
return;
}
setCurrentSlide(slideIndex);
};

return (
<ActionModal
modalVisible={show}
Expand All @@ -209,39 +280,30 @@ const WhatsNewModal = (props) => {
</TouchableOpacity>
</View>
</View>
{!!featuresToShow && (
<ScrollView>
<TouchableWithoutFeedback>
<View>
{featuresToShow.map((feature, index) => (
<View key={index} style={styles.featureContainer}>
<View style={styles.newFeatureImageContainer}>
<Image
source={feature.image}
style={styles.newFeatureImage}
resizeMode={'stretch'}
/>
</View>

<Text style={styles.newFeatureTitle}>
{feature.title}
</Text>
<Text style={styles.newFeatureText}>{feature.text}</Text>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={styles.button}
onPress={() => callButton(feature)}
>
<Text style={styles.buttonText}>
{feature.buttonText}
</Text>
</TouchableOpacity>
</View>
</View>
))}
</View>
</TouchableWithoutFeedback>
</ScrollView>
{featuresToShow && (
<View style={styles.slideContent}>
<ScrollView
// This is not duplicate. Needed for Android.
onScrollEndDrag={onScrollEnd}
onMomentumScrollEnd={onScrollEnd}
showsHorizontalScrollIndicator={false}
horizontal
pagingEnabled
>
{whatsNew.slides.map(renderSlide)}
</ScrollView>
<View style={styles.progessContainer}>
{slideIds.map((id) => (
<View
key={id}
style={[
styles.slideCircle,
currentSlide === id ? styles.slideSolidCircle : {},
]}
/>
))}
</View>
</View>
)}
</View>
</View>
Expand Down
33 changes: 9 additions & 24 deletions app/components/UI/WhatsNewModal/whatsNewList.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,10 @@
import { strings } from '../../../../locales/i18n';
/* eslint-disable import/prefer-default-export */
import { WhatsNew } from './types';

export const whatsNew = [
{
// All users that have <1.0.7 and are updating to >=1.0.7 should see
onlyUpdates: true, // Only users who updated the app will see this, not newly installs
maxLastAppVersion: '1.0.7', // Only users who had a previous version <1.0.7 version will see this
minAppVersion: '1.0.7', // Only users who updated to a version >= 1.0.7 will see this
features: [
{
title: strings('whats_new.feature_security_settings_title'),
text: strings('whats_new.feature_security_settings_text'),
buttonText: strings('whats_new.feature_security_settings_button'),
buttonPress: (props) =>
props.navigation.navigate('SettingsView', {
screen: 'SettingsFlow',
params: { screen: 'SecuritySettings' },
}),
image: require('../../../images/whats-new-security.png'), // eslint-disable-line
},
],
},
];

export default whatsNew;
export const whatsNew: WhatsNew = {
// All users that have <1.0.7 and are updating to >=1.0.7 should see
onlyUpdates: true, // Only users who updated the app will see this, not newly installs
maxLastAppVersion: '1.0.7', // Only users who had a previous version <1.0.7 version will see this
minAppVersion: '1.0.7', // Only users who updated to a version >= 1.0.7 will see this
slides: [[], [], []],
};

0 comments on commit 88d62e0

Please sign in to comment.