diff --git a/src/api/api.js b/src/api/api.js
index 503ecc4ef..b0227d485 100644
--- a/src/api/api.js
+++ b/src/api/api.js
@@ -518,11 +518,17 @@ class API {
}
const headers = {
Authorization: `Bearer ${authToken}`,
- requestOrigin
+ requestOrigin,
+ newVersion: 'true'
};
const { data } = await this.axiosInstance.get(`/subscriber/${userId}`, {
headers
});
+
+ if (data && !data.success) {
+ throw data;
+ }
+
return data;
}
diff --git a/src/components/Board/Board.actions.js b/src/components/Board/Board.actions.js
index cee628515..2602b092b 100644
--- a/src/components/Board/Board.actions.js
+++ b/src/components/Board/Board.actions.js
@@ -758,8 +758,20 @@ export function updateApiObjects(
.then(res => {
const updatedChildBoardId = res.id;
//create - update parent board
+ const updateTilesParentBoard = () =>
+ parentBoard.tiles.map(tile => {
+ if (tile.loadBoard === childBoard.id)
+ return { ...tile, loadBoard: updatedChildBoardId };
+ return tile;
+ });
+ const updatedParentBoard = {
+ ...parentBoard,
+ tiles: createParentBoard
+ ? updateTilesParentBoard()
+ : parentBoard.tiles
+ };
const action = createParentBoard ? createApiBoard : updateApiBoard;
- return dispatch(action(parentBoard, parentBoard.id))
+ return dispatch(action(updatedParentBoard, parentBoard.id))
.then(res => {
const updatedParentBoardId = res.id;
//add new boards to the active communicator
diff --git a/src/components/Board/Board.container.js b/src/components/Board/Board.container.js
index ebee0820f..14afd000e 100644
--- a/src/components/Board/Board.container.js
+++ b/src/components/Board/Board.container.js
@@ -644,7 +644,7 @@ export class BoardContainer extends Component {
// Loggedin user?
if ('name' in userData && 'email' in userData) {
- await this.handleApiUpdates(tile);
+ await this.handleApiUpdates(tile); // this function could mutate tthe tile
return;
}
@@ -1125,6 +1125,7 @@ export class BoardContainer extends Component {
//update the parent
updateBoard(parentBoardData);
}
+ // Untill here all is with shorts ids
//api updates
if (tile && tile.type === 'board') {
//child becomes parent
@@ -1166,6 +1167,9 @@ export class BoardContainer extends Component {
)
.then(parentBoardId => {
if (createParentBoard) {
+ /* Here the parentBoardData is not updated with the values
+ that updatedApiObjects store on the API. Inside the boards are already updated
+ an the value is not replaced because the oldboard Id was replaced on the updateApiObjects inside createApiBoardSuccess */
replaceBoard(
{ ...parentBoardData },
{ ...parentBoardData, id: parentBoardId }
diff --git a/src/components/Board/Board.reducer.js b/src/components/Board/Board.reducer.js
index 03b5fd327..b7db1bd49 100644
--- a/src/components/Board/Board.reducer.js
+++ b/src/components/Board/Board.reducer.js
@@ -71,6 +71,8 @@ function tileReducer(board, action) {
return {
...board,
tiles: [...board.tiles, { ...action.tile }]
+ /* some times when a tile folder is created here the last tile change loadBoard to a long Id with no reason
+ action tile before this copy has a short ID*/
};
case DELETE_TILES:
return {
@@ -165,6 +167,8 @@ function boardReducer(state = initialState, action) {
if (prev.id !== current.id) {
const boardIndex = boards.findIndex(b => b.id === prev.id);
+ /* On create a parent board the prev board doesn't exist with a short Id
+ because is already replaced by a long one */
if (boardIndex >= 0) {
boards[boardIndex] = current;
}
diff --git a/src/components/Settings/Settings.component.js b/src/components/Settings/Settings.component.js
index de13fce71..bbfa9ce4c 100644
--- a/src/components/Settings/Settings.component.js
+++ b/src/components/Settings/Settings.component.js
@@ -25,7 +25,7 @@ import FullScreenDialog from '../UI/FullScreenDialog';
import Paper from '@material-ui/core/Paper';
import UserIcon from '../UI/UserIcon';
import SettingsTour from './SettingsTour.component';
-import { isCordova, isAndroid, isIOS } from '../../cordova-util';
+import { isCordova, isAndroid } from '../../cordova-util';
import './Settings.css';
import { CircularProgress } from '@material-ui/core';
@@ -96,7 +96,7 @@ export class Settings extends PureComponent {
}
];
- if (!isIOS() && !isInFreeCountry) {
+ if (!isInFreeCountry) {
const subscribeSection = {
icon: ,
text: messages.subscribe,
diff --git a/src/components/Settings/Subscribe/Subscribe.component.js b/src/components/Settings/Subscribe/Subscribe.component.js
index 1188b7721..0675b22f0 100644
--- a/src/components/Settings/Subscribe/Subscribe.component.js
+++ b/src/components/Settings/Subscribe/Subscribe.component.js
@@ -9,6 +9,7 @@ import './Subscribe.css';
import SubscriptionInfo from './SubscriptionInfo';
import SubscriptionPlans from './SubscriptionPlans';
+import { CircularProgress } from '@material-ui/core';
const propTypes = {
/**
@@ -29,7 +30,8 @@ const propTypes = {
onRefreshSubscription: PropTypes.func,
onSubscribeCancel: PropTypes.func.isRequired,
onCancelSubscription: PropTypes.func.isRequired,
- cancelSubscriptionStatus: PropTypes.string.isRequired
+ cancelSubscriptionStatus: PropTypes.string.isRequired,
+ updatingStatus: PropTypes.bool.isRequired
};
const defaultProps = {
@@ -46,7 +48,8 @@ const Subscribe = ({
onSubscribeCancel,
onPaypalApprove,
onCancelSubscription,
- cancelSubscriptionStatus
+ cancelSubscriptionStatus,
+ updatingStatus
}) => {
return (
@@ -56,7 +59,11 @@ const Subscribe = ({
onClose={onClose}
// fullWidth
>
- {!subscription.isSubscribed ? (
+ {updatingStatus ? (
+
+
+
+ ) : !subscription.isSubscribed ? (
{
return p.id === product.subscriptionId;
@@ -207,7 +210,10 @@ export class SubscribeContainer extends PureComponent {
try {
await window.CdvPurchase.store.update();
offers = prod.offers;
- offer = offers.find(offer => offer.tags[0] === product.tag);
+ const findCallback = isAndroid()
+ ? offer => offer.tags[0] === product.tag
+ : offer => offer.productId === product.subscriptionId;
+ offer = offers.find(findCallback);
} catch (err) {
console.error('Cannot subscribe product. Error: ', err.message);
this.handleError(err);
@@ -215,6 +221,11 @@ export class SubscribeContainer extends PureComponent {
}
}
+ const filterInAppPurchaseIOSTransactions = uniqueReceipt =>
+ uniqueReceipt.transactions.filter(
+ transaction => transaction.transactionId !== 'appstore.application'
+ );
+
try {
// update the api
const requestOrigin =
@@ -225,40 +236,80 @@ export class SubscribeContainer extends PureComponent {
// check if current subscriber already bought in this device
if (localReceipts.length) {
const lastReceipt = localReceipts.slice(-1)[0];
- if (
- lastReceipt &&
- lastReceipt?.transactions[0]?.nativePurchase?.orderId !==
- subscriber.transaction?.transactionId
- ) {
- this.handleError({
- code: '0001',
- message: intl.formatMessage(messages.googleAccountAlreadyOwns)
- });
- return;
+ if (isAndroid()) {
+ if (
+ lastReceipt &&
+ lastReceipt?.transactions[0]?.nativePurchase?.orderId !==
+ subscriber.transaction?.transactionId
+ ) {
+ this.handleError({
+ code: '0001',
+ message: intl.formatMessage(messages.googleAccountAlreadyOwns)
+ });
+ return;
+ }
+ }
+ if (isIOS()) {
+ //IOS have a unique receipt here => 'lastReceipt'
+ const inAppPurchaseTransactions = filterInAppPurchaseIOSTransactions(
+ localReceipts[0]
+ );
+
+ const lastTransaction = inAppPurchaseTransactions.slice(-1)[0];
+ if (
+ inAppPurchaseTransactions.length > 0 &&
+ lastTransaction?.transactionId !==
+ subscriber.transaction?.transactionId
+ ) {
+ this.handleError({
+ code: '0001',
+ message: intl.formatMessage(messages.appleAccountAlreadyOwns)
+ });
+ return;
+ }
}
}
+
await API.updateSubscriber(apiProduct);
// proceed with the purchase
- if (isAndroid()) {
+ if (isAndroid() || isIOS()) {
const order = await window.CdvPurchase.store.order(offer);
if (order && order.isError) throw order;
updateSubscription({
ownedProduct: {
...product,
- platform: 'android-playstore'
+ platform: isAndroid() ? 'android-playstore' : 'app-store'
}
});
}
} catch (err) {
- if (err.response?.data.error === 'subscriber not found') {
+ const isSubscriberNotFound =
+ (err.response?.data?.error || err.error) === 'subscriber not found';
+
+ if (isSubscriberNotFound) {
// check if current subscriber already bought in this device
- if (localReceipts.length) {
- this.handleError({
- code: '0001',
- message: intl.formatMessage(messages.googleAccountAlreadyOwns)
- });
- return;
+ if (isAndroid()) {
+ if (localReceipts.length) {
+ this.handleError({
+ code: '0001',
+ message: intl.formatMessage(messages.googleAccountAlreadyOwns)
+ });
+ return;
+ }
+ }
+ if (isIOS()) {
+ const localInAppPurchaseTransactions = filterInAppPurchaseIOSTransactions(
+ localReceipts[0]
+ );
+
+ if (localInAppPurchaseTransactions.length) {
+ this.handleError({
+ code: '0001',
+ message: intl.formatMessage(messages.appleAccountAlreadyOwns)
+ });
+ return;
+ }
}
try {
const newSubscriber = {
@@ -269,13 +320,13 @@ export class SubscribeContainer extends PureComponent {
};
const res = await API.createSubscriber(newSubscriber);
updateSubscriberId(res._id);
- if (isAndroid()) {
+ if (isAndroid() || isIOS()) {
const order = await window.CdvPurchase.store.order(offer);
if (order && order.isError) throw order;
updateSubscription({
ownedProduct: {
...product,
- platform: 'android-playstore'
+ platform: isAndroid() ? 'android-playstore' : 'app-store'
}
});
}
@@ -306,6 +357,7 @@ export class SubscribeContainer extends PureComponent {
onPaypalApprove={this.handlePaypalApprove}
onCancelSubscription={this.handleCancelSubscription}
cancelSubscriptionStatus={this.state.cancelSubscriptionStatus}
+ updatingStatus={this.state.updatingStatus}
/>
);
}
diff --git a/src/components/Settings/Subscribe/Subscribe.css b/src/components/Settings/Subscribe/Subscribe.css
index 687df3158..97b778ad0 100644
--- a/src/components/Settings/Subscribe/Subscribe.css
+++ b/src/components/Settings/Subscribe/Subscribe.css
@@ -1,3 +1,10 @@
+.Subscribe__Loading__Container {
+ padding: 1em;
+ height: 92vh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
.Subscribe__Alert {
margin: 8px 0;
}
diff --git a/src/components/Settings/Subscribe/Subscribe.messages.js b/src/components/Settings/Subscribe/Subscribe.messages.js
index 37d4e4c6d..08fa05288 100644
--- a/src/components/Settings/Subscribe/Subscribe.messages.js
+++ b/src/components/Settings/Subscribe/Subscribe.messages.js
@@ -89,6 +89,11 @@ export default defineMessages({
id: 'cboard.components.Settings.Subscribe.not_subscribed',
defaultMessage: 'You are not subscribed. '
},
+ unverified: {
+ id: 'cboard.components.Settings.Subscribe.unverified',
+ defaultMessage:
+ 'An error occurred during validation of your purchase. Please refresh'
+ },
refresh: {
id: 'cboard.components.Settings.Subscribe.refresh',
defaultMessage: 'Refresh'
@@ -148,6 +153,11 @@ export default defineMessages({
defaultMessage:
'It looks like your Google account has already purchased a product. Try restarting the app.'
},
+ appleAccountAlreadyOwns: {
+ id: 'cboard.components.Settings.Subscribe.appleAccountAlreadyOwns',
+ defaultMessage:
+ 'It looks like your Apple account has already purchased a product. Try restarting the app.'
+ },
fallback: {
id: 'cboard.components.Settings.Subscribe.fallback',
defaultMessage: 'Wait please...'
diff --git a/src/components/Settings/Subscribe/SubscriptionInfo.js b/src/components/Settings/Subscribe/SubscriptionInfo.js
index 40589f125..f90e62fdd 100644
--- a/src/components/Settings/Subscribe/SubscriptionInfo.js
+++ b/src/components/Settings/Subscribe/SubscriptionInfo.js
@@ -29,8 +29,8 @@ import {
import RefreshIcon from '@material-ui/icons/Refresh';
import IconButton from '../../UI/IconButton';
-import { isAndroid, isElectron } from '../../../cordova-util';
-import { GOOGLE_PLAY_STORE_URL } from './Subscribe.constants';
+import { isAndroid, isElectron, isIOS } from '../../../cordova-util';
+import { APP_STORE_URL, GOOGLE_PLAY_STORE_URL } from './Subscribe.constants';
const propTypes = {
ownedProduct: PropTypes.object.isRequired,
@@ -131,13 +131,15 @@ const SubscriptionInfo = ({