add support to show promo

This commit is contained in:
ammarahm-ed
2021-04-11 10:34:01 +05:00
parent 777d605dac
commit 84d2b72fab
13 changed files with 245 additions and 44 deletions

View File

@@ -322,6 +322,8 @@ export const AppRootEvents = React.memo(
await PremiumService.setPremiumStatus();
setLoginMessage(dispatch);
}
} catch (e) {
let user = await db.user.getUser();
if (user && !user.isEmailConfirmed) {
@@ -336,6 +338,9 @@ export const AppRootEvents = React.memo(
if (login) {
eSendEvent(eCloseProgressDialog);
}
let announcement = await db.announcement();
}
};

View File

@@ -185,8 +185,8 @@ export class DialogManager extends Component {
eUnSubscribeEvent(eClosePremiumDialog, this.hidePremiumDialog);
}
showPremiumDialog = () => {
this.premiumDialog.open();
showPremiumDialog = (prompoInfo) => {
this.premiumDialog.open(prompoInfo);
};
hidePremiumDialog = () => {

View File

@@ -7,14 +7,17 @@ class PremiumDialog extends React.Component {
super(props);
this.state = {
visible: false,
promo:null
};
this.actionSheetRef = createRef();
}
open() {
open(promoInfo) {
console.log(promoInfo)
this.setState(
{
visible: true,
promo:promoInfo
},
() => {
this.actionSheetRef.current?.setModalVisible(true);
@@ -35,7 +38,7 @@ class PremiumDialog extends React.Component {
render() {
return !this.state.visible ? null : (
<ActionSheetWrapper onClose={this.onClose} fwdRef={this.actionSheetRef}>
<PremiumComponent close={this.close} />
<PremiumComponent promo={this.state.promo} close={this.close} />
</ActionSheetWrapper>
);
}

View File

@@ -10,6 +10,7 @@ import {eCloseResultDialog, eOpenResultDialog} from '../../utils/Events';
import {ph, SIZE} from '../../utils/SizeUtils';
import {Button} from '../Button';
import BaseDialog from '../Dialog/base-dialog';
import {SvgToPngView} from '../ListPlaceholders';
import Seperator from '../Seperator';
import Heading from '../Typography/Heading';
import Paragraph from '../Typography/Paragraph';
@@ -33,7 +34,7 @@ const ResultDialog = () => {
};
}, []);
const open = (data) => {
const open = data => {
setDialogData(data);
setVisible(true);
};
@@ -56,7 +57,13 @@ const ResultDialog = () => {
justifyContent: 'center',
alignItems: 'center',
}}>
<SvgXml xml={WELCOME_SVG(colors.accent)} width={170} height={170} />
<SvgToPngView
src={WELCOME_SVG(colors.accent)}
color={colors.accent}
img="welcome"
width={170}
height={170}
/>
<Heading
size={SIZE.lg}

View File

@@ -0,0 +1,83 @@
import React, {useEffect} from 'react';
import {View} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import {useTracked} from '../../provider';
import {eSendEvent} from '../../services/EventManager';
import {eOpenPremiumDialog} from '../../utils/Events';
import {openLinkInBrowser} from '../../utils/functions';
import {SIZE} from '../../utils/SizeUtils';
import useAnnouncement from '../../utils/useAnnouncement';
import {Button} from '../Button';
import Seperator from '../Seperator';
import Heading from '../Typography/Heading';
import Paragraph from '../Typography/Paragraph';
export const Announcement = ({data, color}) => {
const [state] = useTracked();
const {selectionMode} = state;
const [announcement, remove] = useAnnouncement();
return !announcement || selectionMode ? null : (
<View
style={{
backgroundColor: color,
width: '100%',
}}>
<View
style={{
paddingHorizontal: 12,
paddingVertical: 12,
width: '100%',
}}>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
}}>
{announcement?.title && (
<Heading
style={{
width: '90%',
}}
size={SIZE.lg}
color="white">
{announcement.title}
</Heading>
)}
<Icon onPress={remove} name="close" size={SIZE.xl} color="white" />
</View>
{announcement?.description && (
<Paragraph color="white">{announcement.description}</Paragraph>
)}
<Seperator />
{announcement?.cta && (
<Button
type="inverted"
title={announcement.cta.text}
fontSize={SIZE.md}
onPress={async () => {
if (announcement.cta.type === 'link') {
try {
await openLinkInBrowser(
announcement.cta.action,
state.colors,
);
} catch (e) {}
} else if (announcement.cta.type === 'promo') {
eSendEvent(eOpenPremiumDialog, {
promoCode: announcement.cta.action,
text: announcement.cta.text,
});
}
}}
width="100%"
/>
)}
</View>
</View>
);
};

View File

@@ -12,6 +12,8 @@ export const Card = ({data, color}) => {
const [state] = useTracked();
const {selectionMode, messageBoardState} = state;
return !messageBoardState.visible || selectionMode ? null : (
<TouchableOpacity
activeOpacity={0.8}
@@ -24,7 +26,7 @@ export const Card = ({data, color}) => {
position: DDS.isLargeTablet() ? 'relative' : 'absolute',
right: 0,
top: 0,
zIndex: 999,
zIndex: 100,
backgroundColor: messageBoardState.type === 'error' ? 'red' : color,
width: '100%',
}}>

View File

@@ -8,6 +8,7 @@ import { normalize, SIZE } from '../../utils/SizeUtils';
import {Button} from '../Button';
import {Placeholder} from '../ListPlaceholders';
import Heading from '../Typography/Heading';
import {Announcement} from './announcement';
import {Card} from './card';
export const Header = ({

View File

@@ -10,11 +10,13 @@ import Navigation from '../../services/Navigation';
import SettingsService from '../../services/SettingsService';
import Sync from '../../services/Sync';
import {dHeight} from '../../utils';
import {COLORS_NOTE} from '../../utils/Colors';
import {eScrollEvent} from '../../utils/Events';
import {sleep} from '../../utils/TimeUtils';
import {NotebookWrapper} from '../NotebookItem/wrapper';
import {NoteWrapper} from '../NoteItem/wrapper';
import TagItem from '../TagItem';
import {Announcement} from './announcement';
import {Empty} from './empty';
import {Footer} from './footer';
import {Header} from './header';
@@ -69,9 +71,14 @@ const SimpleList = ({
),
);
setLoading(false);
setTimeout(() => {
setLoaded(true)
},Navigation.getCurrentScreen() === SettingsService.get().homepage ? 1000 : 150);
setTimeout(
() => {
setLoaded(true);
},
Navigation.getCurrentScreen() === SettingsService.get().homepage
? 1000
: 150,
);
}
}, [listData, deviceMode, loading]);
@@ -82,14 +89,14 @@ const SimpleList = ({
}
};
const _onScroll = (event) => {
const _onScroll = event => {
if (!event) return;
let y = event.nativeEvent.contentOffset.y;
eSendEvent(eScrollEvent, y);
};
const _layoutProvider = new LayoutProvider(
(index) => {
index => {
return dataProvider.getDataForIndex(index).type;
},
(type, dim) => {
@@ -230,6 +237,9 @@ const SimpleList = ({
};
return (
<>
<Announcement
color={COLORS_NOTE[headerProps.heading?.toLowerCase()] || colors.accent}
/>
{loaded && !loading ? null : (
<>
<View
@@ -240,6 +250,11 @@ const SimpleList = ({
backgroundColor: colors.bg,
zIndex: 999,
}}>
<Announcement
color={
COLORS_NOTE[headerProps.heading?.toLowerCase()] || colors.accent
}
/>
<Header
title={headerProps.heading}
paragraph={headerProps.paragraph}

View File

@@ -25,6 +25,8 @@ import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import {DDS} from '../../services/DeviceDetection';
import {openLinkInBrowser} from '../../utils/functions';
import {Modal} from 'react-native';
import {SafeAreaView} from 'react-native';
import {SvgToPngView} from '../ListPlaceholders';
const features = [
{
@@ -39,6 +41,7 @@ const features = [
'Your data is encrypted on your device. No one but you can read your notes.',
icon: PRIVACY_SVG,
link: 'https://notesnook.com',
img:"privacy"
},
{
icon: SYNC_SVG,
@@ -46,6 +49,7 @@ const features = [
description:
'Everything is automatically synced to all your devices in a safe and secure way. Notesnook is available on all major platforms.',
link: 'https://notesnook.com',
img:'sync'
},
{
icon: ORGANIZE_SVG,
@@ -53,6 +57,7 @@ const features = [
description:
'Add your notes in notebooks and topics or simply assign tags or colors to find them easily.',
link: 'https://notesnook.com',
img:"sync"
},
{
icon: COMMUNITY_SVG,
@@ -60,6 +65,7 @@ const features = [
description:
'We are not ghosts, chat with us and share your experience. Give suggestions, report issues and meet other people using Notesnook',
link: 'https://discord.gg/zQBK97EE22',
img:'community'
},
];
let currentIndex = 0;
@@ -75,7 +81,7 @@ const SplashScreen = () => {
useEffect(() => {
Storage.read('introCompleted').then(async r => {
requestAnimationFrame(() => {
if (!r) {
if (r) {
setVisible(true);
timing(opacity, {
toValue: 1,
@@ -94,7 +100,7 @@ const SplashScreen = () => {
return (
visible && (
<Modal animationType="slide" statusBarTranslucent visible>
<Animated.View
<SafeAreaView
style={{
width: '100%',
height: '100%',
@@ -119,8 +125,7 @@ const SplashScreen = () => {
onSnapToItem={i => {
currentIndex = i;
}}
activeAnimationType="timing"
shouldOptimizeUpdates
maxToRenderPerBatch={10}
renderItem={({item, index}) => (
<View
style={{
@@ -155,12 +160,14 @@ const SplashScreen = () => {
size={170}
/>
) : (
<SvgXml
xml={
<SvgToPngView
src={
item.icon
? item.icon(colors.accent)
: NOTE_SVG(colors.accent)
}
img={item.img}
color={colors.accent}
width={250}
height={250}
/>
@@ -249,7 +256,7 @@ const SplashScreen = () => {
/>
</View>
</Animated.View>
</Animated.View>
</SafeAreaView>
</Modal>
)
);

View File

@@ -20,7 +20,7 @@ export const UpdateDialog = () => {
const [visible, setVisible] = useState(false);
const [version, setVersion] = useState(null);
const open = (version) => {
const open = version => {
setVersion(version);
setVisible(true);
};
@@ -33,8 +33,8 @@ export const UpdateDialog = () => {
};
}, []);
const format = (ver) => {
if (!ver) return ""
const format = ver => {
if (!ver) return '';
let parts = ver.toString().split('');
return `v${parts[0]}.${parts[1]}.${parts[2]}${
parts[3] === '0' ? '' : parts[3]
@@ -45,7 +45,7 @@ export const UpdateDialog = () => {
visible && (
<BaseDialog
onRequestClose={() => {
setVisible(false);
version.severity !== 2 && setVisible(false);
}}
visible={true}>
<DialogContainer>
@@ -114,9 +114,7 @@ export const UpdateDialog = () => {
let url_android =
'https://play.google.com/store/apps/details?id=com.streetwriters.notesnook';
let url_ios = 'itms-apps://itunes.apple.com/app/id1544027013';
setVisible(false);
version.severity !== 2 && setVisible(false);
await Linking.openURL(
Platform.OS === 'android' ? url_android : url_ios,
);

View File

@@ -14,15 +14,18 @@ import {itemSkus} from '../utils';
let premiumStatus = 0;
let products = [];
let user = null
function getUser() {
return user;
}
async function setPremiumStatus() {
try {
let user = await db.user.getUser();
user = await db.user.getUser();
if (!user) {
premiumStatus = null;
updateEvent({type: Actions.PREMIUM, state: get()});
} else {
premiumStatus = user.subscription.type;
updateEvent({type: Actions.PREMIUM, state: get()});
@@ -172,5 +175,6 @@ export default {
get,
onUserStatusCheck,
showVerifyEmailDialog,
getProducts
getProducts,
getUser
};

View File

@@ -26,7 +26,7 @@ export async function setSetting(settings, name, value) {
export const scrollRef = createRef();
export const AndroidModule = NativeModules.NNativeModule;
export const getElevation = (elevation) => {
export const getElevation = elevation => {
return {
elevation,
shadowColor: 'black',
@@ -154,8 +154,9 @@ export const SUBSCRIPTION_STATUS = {
BASIC: 0,
TRIAL: 1,
BETA: 2,
TRIAL_EXPIRED: 3,
BETA_EXPIRED: 4,
PREMIUM: 5,
PREMIUM_EXPIRED: 6,
PREMIUM_CANCELLED: 7,
};
export const SUBSCRIPTION_STATUS_STRINGS = {

View File

@@ -0,0 +1,75 @@
import {useCallback, useEffect, useState} from 'react';
import {Platform} from 'react-native';
import {SUBSCRIPTION_STATUS} from '.';
import PremiumService from '../services/PremiumService';
import { db } from './DB';
import Storage from './storage';
var CACHED_ANNOUNCEMENT;
export default function useAnnouncement() {
const [announcement, setAnnouncement] = useState();
useEffect(() => {
(async function () {
try {
CACHED_ANNOUNCEMENT = CACHED_ANNOUNCEMENT || (await db.announcement());
if (
!CACHED_ANNOUNCEMENT ||
await Storage.read('removedAnnouncement') === CACHED_ANNOUNCEMENT.id ||
!shouldShowAnnouncement(CACHED_ANNOUNCEMENT)
)
return;
setAnnouncement(CACHED_ANNOUNCEMENT);
} catch(e) {
setAnnouncement()
}
})();
}, []);
const remove = useCallback(async () => {
await Storage.write('removedAnnouncement', CACHED_ANNOUNCEMENT.id);
setAnnouncement();
}, [announcement]);
return [announcement, remove];
}
const allowedPlatforms = ['all', 'mobile', Platform.OS];
function shouldShowAnnouncement(announcement) {
let show = allowedPlatforms.indexOf(announcement.platform) > -1;
console.log(show)
if (!show) return;
const subStatus = PremiumService.getUser()?.subscription?.type;
switch (announcement.userType) {
case 'pro':
show = isUserPremium();
break;
case 'trial':
show = subStatus === SUBSCRIPTION_STATUS.TRIAL;
break;
case 'trialExpired':
show = subStatus === SUBSCRIPTION_STATUS.BASIC;
break;
case 'loggedOut':
show = !PremiumService.getUser();
break;
case 'unverified':
show = !PremiumService.getUser()?.isEmailVerified;
break;
case 'proExpired':
show =
subStatus === SUBSCRIPTION_STATUS.PREMIUM_EXPIRED ||
subStatus === SUBSCRIPTION_STATUS.PREMIUM_CANCELED;
break;
case 'any':
default:
break;
}
return show;
}