diff --git a/apps/mobile/App.js b/apps/mobile/App.js
index dfb7e0ada..d9ea5af3f 100644
--- a/apps/mobile/App.js
+++ b/apps/mobile/App.js
@@ -33,7 +33,7 @@ async function loadDefaultNotes() {
try {
const isCreated = await MMKV.getItem('defaultNoteCreated');
if (isCreated) return;
- const notes = await http.get('https://app.notesnook.com/notes/index.json');
+ const notes = await http.get('https://app.notesnook.com/notes/index_14.json');
if (!notes) return;
for (let note of notes) {
const content = await http.get(note.mobileContent);
diff --git a/apps/mobile/AppRootEvents.js b/apps/mobile/AppRootEvents.js
index 20b5e066d..e857b635c 100644
--- a/apps/mobile/AppRootEvents.js
+++ b/apps/mobile/AppRootEvents.js
@@ -5,6 +5,7 @@ import {Appearance, AppState, Linking, Platform} from 'react-native';
import RNExitApp from 'react-native-exit-app';
import * as RNIap from 'react-native-iap';
import {enabled} from 'react-native-privacy-snapshot';
+import {abs} from 'react-native-reanimated';
import SplashScreen from 'react-native-splash-screen';
import {
clearAllStores,
@@ -250,8 +251,8 @@ export const AppRootEvents = React.memo(
};
const onAccountStatusChange = async userStatus => {
- console.log('account status', userStatus, PremiumService.get());
if (!PremiumService.get() && userStatus.type === 5) {
+ PremiumService.subscriptions.clear();
eSendEvent(eOpenProgressDialog, {
title: 'Notesnook Pro',
paragraph: `Your Notesnook Pro subscription has been successfully activated.`,
@@ -375,11 +376,8 @@ export const AppRootEvents = React.memo(
};
const onSuccessfulSubscription = async subscription => {
- const receipt = subscription.transactionReceipt;
- if (prevTransactionId === subscription.transactionId) {
- return;
- }
- await processReceipt(receipt);
+ await PremiumService.subscriptions.set(subscription);
+ await PremiumService.subscriptions.verify(subscription);
};
const onSubscriptionError = async error => {
@@ -389,46 +387,6 @@ export const AppRootEvents = React.memo(
message: error.message,
context: 'local',
});
-
- if (Platform.OS === 'ios') {
- await RNIap.clearTransactionIOS();
- }
- };
-
- const processReceipt = async receipt => {
- if (receipt) {
- if (Platform.OS === 'ios') {
- let user = await db.user.getUser();
- if (!user) return;
- fetch('https://payments.streetwriters.co/apple/verify', {
- method: 'POST',
- body: JSON.stringify({
- receipt_data: receipt,
- user_id: user.id,
- }),
- headers: {
- 'Content-Type': 'application/json',
- },
- })
- .then(async r => {
- let text = await r.text();
- console.log(r.ok, text);
- if (!r.ok) {
- if (text === 'Receipt already expired.') {
- console.log('RNIap.clearTransactionIOS');
- await RNIap.clearTransactionIOS();
- }
- return;
- }
- console.log('Success', 'RNIap.finishTransactionIOS');
- await RNIap.finishTransactionIOS(prevTransactionId);
- await RNIap.clearTransactionIOS();
- })
- .catch(e => {
- console.log(e, 'ERROR');
- });
- }
- }
};
const onAppStateChanged = async state => {
diff --git a/apps/mobile/ios/Notesnook.xcodeproj/project.pbxproj b/apps/mobile/ios/Notesnook.xcodeproj/project.pbxproj
index 17eae66ed..e857f9cce 100644
--- a/apps/mobile/ios/Notesnook.xcodeproj/project.pbxproj
+++ b/apps/mobile/ios/Notesnook.xcodeproj/project.pbxproj
@@ -1036,7 +1036,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1420;
+ CURRENT_PROJECT_VERSION = 1430;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
@@ -1108,7 +1108,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.4.2;
+ MARKETING_VERSION = 1.4.3;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1136,7 +1136,7 @@
CODE_SIGN_ENTITLEMENTS = Notesnook/Notesnook.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
- CURRENT_PROJECT_VERSION = 1420;
+ CURRENT_PROJECT_VERSION = 1430;
DEVELOPMENT_TEAM = 53CWBG3QUC;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
HEADER_SEARCH_PATHS = (
@@ -1207,7 +1207,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.4.2;
+ MARKETING_VERSION = 1.4.3;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1363,7 +1363,7 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1420;
+ CURRENT_PROJECT_VERSION = 1430;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
GCC_C_LANGUAGE_STANDARD = gnu11;
@@ -1436,7 +1436,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 1.4.2;
+ MARKETING_VERSION = 1.4.3;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1466,7 +1466,7 @@
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
- CURRENT_PROJECT_VERSION = 1420;
+ CURRENT_PROJECT_VERSION = 1430;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 53CWBG3QUC;
GCC_C_LANGUAGE_STANDARD = gnu11;
@@ -1539,7 +1539,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 1.4.2;
+ MARKETING_VERSION = 1.4.3;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";
diff --git a/apps/mobile/ios/Podfile.lock b/apps/mobile/ios/Podfile.lock
index 06c564e4e..f50d889fc 100644
--- a/apps/mobile/ios/Podfile.lock
+++ b/apps/mobile/ios/Podfile.lock
@@ -265,7 +265,7 @@ PODS:
- React
- react-native-splash-screen (3.2.0):
- React
- - react-native-webview (11.4.0):
+ - react-native-webview (11.6.4):
- React-Core
- React-perflogger (0.64.2)
- React-RCTActionSheet (0.64.2):
@@ -659,7 +659,7 @@ SPEC CHECKSUMS:
react-native-safe-area-context: b6e0e284002381d2ff29fa4fff42b4d8282e3c94
react-native-sodium: c7587732667e1cdb7d0d77c2aa2e98420aad2036
react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
- react-native-webview: 4dfb534e9600b87fe667c5ca4fe09149383218b9
+ react-native-webview: 1a19adb5578cdf7f005b7961dcc50c1c6b70f41b
React-perflogger: 25373e382fed75ce768a443822f07098a15ab737
React-RCTActionSheet: af7796ba49ffe4ca92e7277a5d992d37203f7da5
React-RCTAnimation: 6a2e76ab50c6f25b428d81b76a5a45351c4d77aa
diff --git a/apps/mobile/src/services/PremiumService.js b/apps/mobile/src/services/PremiumService.js
index 6dcc3c6e0..9df524de4 100644
--- a/apps/mobile/src/services/PremiumService.js
+++ b/apps/mobile/src/services/PremiumService.js
@@ -37,6 +37,8 @@ async function setPremiumStatus() {
if (!get()) {
await RNIap.initConnection();
products = await RNIap.getSubscriptions(itemSkus);
+ } else {
+ await subscriptions.clear();
}
}
}
@@ -159,6 +161,102 @@ const showVerifyEmailDialog = () => {
});
};
+const subscriptions = {
+ get: async () => {
+ let _subscriptions = await MMKV.getItem('subscriptionsIOS');
+ if (!_subscriptions) return [];
+ return JSON.parse(_subscriptions);
+ },
+ set: async subscription => {
+ let _subscriptions = await MMKV.getItem('subscriptionsIOS');
+ if (_subscriptions) {
+ _subscriptions = JSON.parse(_subscriptions);
+ } else {
+ _subscriptions = [];
+ }
+ let index = _subscriptions.findIndex(
+ s => s.transactionId === transactionId,
+ );
+ if (index === -1) {
+ _subscriptions.unshift(subscription);
+ } else {
+ _subscriptions[index] = subscription;
+ }
+ await MMKV.setItem('subscriptionsIOS', JSON.stringify(_subscriptions));
+ },
+ remove: async transactionId => {
+ let _subscriptions = await MMKV.getItem('subscriptionsIOS');
+ if (_subscriptions) {
+ _subscriptions = JSON.parse(_subscriptions);
+ } else {
+ _subscriptions = [];
+ }
+ let index = _subscriptions.findIndex(
+ s => s.transactionId === transactionId,
+ );
+ if (index !== -1) {
+ _subscriptions.splice(index);
+ await MMKV.setItem('subscriptionsIOS', JSON.stringify(_subscriptions));
+ }
+ },
+ verify: async subscription => {
+ console.log(
+ 'verifying: ',
+ subscription.transactionId,
+ new Date(subscription.transactionDate).toLocaleString(),
+ );
+
+ if (subscription.transactionReceipt) {
+ if (Platform.OS === 'ios') {
+ let user = await db.user.getUser();
+ if (!user) return;
+ let requestData = {
+ method: 'POST',
+ body: JSON.stringify({
+ receipt_data: subscription.transactionReceipt,
+ user_id: user.id,
+ }),
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ };
+ try {
+ let result = await fetch(
+ 'https://payments.streetwriters.co/apple/verify',
+ requestData,
+ );
+ let text = await result.text();
+ if (!result.ok) {
+ if (text === 'Receipt already expired.') {
+ await subscriptions.clear(subscription);
+ console.log('clearing because expired');
+ }
+ return;
+ }
+ } catch (e) {
+ console.log('subscription error', e);
+ }
+ }
+ }
+ },
+ clear: async _subscription => {
+ let _subscriptions = await subscriptions.get();
+ let subscription = null;
+ if (_subscription) {
+ subscription = _subscription;
+ console.log('got id to clear');
+ } else {
+ subscription = _subscriptions.length > 0 ? _subscriptions[0] : null;
+ }
+ if (subscription) {
+ await RNIap.finishTransactionIOS(subscription.transactionId);
+ await RNIap.clearTransactionIOS();
+ await subscriptions.remove(subscription.transactionId);
+ console.log('clearing subscriptions');
+ }
+ },
+};
+
export default {
verify,
setPremiumStatus,
@@ -167,4 +265,5 @@ export default {
showVerifyEmailDialog,
getProducts,
getUser,
+ subscriptions,
};
diff --git a/apps/mobile/src/views/Settings/index.js b/apps/mobile/src/views/Settings/index.js
index 225dbbaeb..ccc412a3a 100644
--- a/apps/mobile/src/views/Settings/index.js
+++ b/apps/mobile/src/views/Settings/index.js
@@ -73,6 +73,7 @@ import {
import {hexToRGBA, RGB_Linear_Shade} from '../../utils/ColorUtils';
import {db} from '../../utils/DB';
import {
+ eCloseProgressDialog,
eOpenLoginDialog,
eOpenPremiumDialog,
eOpenProgressDialog,
@@ -88,6 +89,7 @@ import {pv, SIZE} from '../../utils/SizeUtils';
import Storage from '../../utils/storage';
import {sleep, timeConverter} from '../../utils/TimeUtils';
import ToggleSwitch from 'toggle-switch-react-native';
+import * as RNIap from 'react-native-iap';
let menuRef = createRef();
@@ -703,7 +705,10 @@ const SettingsUserSection = () => {
};
const manageSubscription = () => {
- if (user.subscription.type === SUBSCRIPTION_STATUS.PREMIUM_CANCELLED) {
+ if (
+ user.subscription.type === SUBSCRIPTION_STATUS.PREMIUM_CANCELLED &&
+ Platform.OS === 'android'
+ ) {
if (user.subscription.provider === 3) {
ToastEvent.show({
heading: 'Subscribed on web',
@@ -912,12 +917,9 @@ const SettingsUserSection = () => {
SUBSCRIPTION_STATUS.PREMIUM_CANCELLED
? 'Manage subscription from desktop app'
: user.subscription.type ===
- SUBSCRIPTION_STATUS.PREMIUM_CANCELLED
- ? `Resubscribe from ${
- Platform.OS === 'ios'
- ? 'App Store'
- : 'Google Playstore'
- }`
+ SUBSCRIPTION_STATUS.PREMIUM_CANCELLED &&
+ Platform.OS === 'android'
+ ? `Resubscribe from Google Playstore`
: user.subscription.type ===
SUBSCRIPTION_STATUS.PREMIUM_EXPIRED
? `Resubscribe to Notesnook Pro (${
@@ -1058,15 +1060,52 @@ const SettingsUserSection = () => {
},
desc: 'Try force sync to resolve issues with syncing.',
},
- ].map(item => (
-
- ))}
+ {
+ name: 'Subscription not activated?',
+ func: async () => {
+ eSendEvent(eOpenProgressDialog, {
+ title: 'Loading subscriptions',
+ paragraph: `Please wait while we fetch your subscriptions.`,
+ });
+ let subscriptions = await RNIap.getPurchaseHistory();
+ subscriptions.sort(
+ (a, b) => b.transactionDate - a.transactionDate,
+ );
+ let currentSubscription = subscriptions[0];
+ eSendEvent(eOpenProgressDialog, {
+ title: 'Notesnook Pro',
+ paragraph: `You subscribed to Notesnook Pro on ${new Date(
+ currentSubscription.transactionDate,
+ ).toLocaleString()}. Verify this subscription?`,
+ action: async () => {
+ eSendEvent(eOpenProgressDialog, {
+ title: 'Verifying subscription',
+ paragraph: `Please wait while we verify your subscription.`,
+ });
+ await PremiumService.subscriptions.verify(
+ currentSubscription,
+ );
+ eSendEvent(eCloseProgressDialog);
+ },
+ icon: 'information-outline',
+ actionText: 'Verify',
+ noProgress: true,
+ });
+ },
+ desc: 'Verify your subscription to Notesnook Pro',
+ },
+ ].map(item =>
+ item.name === 'Subscription not activated?' &&
+ (Platform.OS !== 'ios' || PremiumService.get()) ? null : (
+
+ ),
+ )}
>
) : null}
>