0

We have to break the checkout flow in our mobile app and move it outside the app. So currently we have it setup that tapping Cart & Account will result in their default browser opening to the websites corresponding URL. However we are struggling with getting the Add To Cart Button to open their default browser to the products corresponding URL on the website.

ProductSingleScreen.js

renderAddToCart = (product) => {
        //console.log(this.props)
        const { purchasable, isOutOfStock, soldIndividually } = product;
        const {isAddingToCart} = this.props
        //console.log(this.props.isAddingToCart)
        if(purchasable) {
            if (isOutOfStock) {
                return (
                    <Text style={styles.addToCartButtonText} uppercase={false}>{I18n.t('Out of Stock!')}</Text>
                )
            }

            return (
                <Row style={styles.addToCartbuttonContainer}>
                {
                    !Boolean(soldIndividually) &&
                    <Col style={styles.quantityButtonsContainer}>
                        <Row>
                            <Col><Button style={styles.quantityAddRemove} bordered full onPress={this.props.decreaseQty}>
                                <Icon type={'Ionicons'} name={'ios-remove'}></Icon>
                            </Button></Col>
                            <Col style={styles.quantityInputBox}><Text>{this.props.getQtyVal}</Text></Col>
                            <Col><Button bordered block style={styles.quantityAddRemove} onPress={this.props.increaseQty}>
                                <Icon name={'ios-add'}></Icon>
                            </Button></Col>
                        </Row>
                    </Col>
                }
                <Col style={styles.addButtons}>
                    <Button disabled={(isAddingToCart || (product.type == 'variable' && !this.state.variation_id)) ? true : false } full onPress={() => this.processAddToCart(product)}>
                        {isAddingToCart && <ActivityIndicator size="large" color="#ffffff" />}
                        {!isAddingToCart && <Text style={styles.addToCartButtonText} uppercase={false}>{I18n.t('Add To Cart')}</Text>}
                    </Button>
                </Col>
            </Row>
            );
        }
    };

    renderEnternalProductButton =(product) => {
        const { purchasable, button_text, external_url } = product;
        if(!purchasable) {
            return (
            <Row style={styles.addToCartbuttonContainer}>
                <Col style={styles.addButtons}>
                    <Button full onPress={ ()=> Linking.openURL(external_url) }>
                        <Text style={styles.addToCartButtonText} uppercase={false}>{button_text}</Text>
                    </Button>
                </Col>
            </Row>
            )
        }
    }

    renderCategories = (product) => {
        const { categories } = product
        if(categories.length == 0 ) return null
        return (
            <View style={styles.categoryList}>
                <View style={styles.categoryLabel} >
                    { categories.length == 1 && <Text style={styles.categoryLabelText}>{I18n.t('Category')}:</Text> }
                    { categories.length > 1 && <Text style={styles.categoryLabelText}>{I18n.t('Categories')}:</Text> }
                </View>
                {
                    categories.map((category, index) => {
                        return(
                            <View style={styles.singleCategory} key={category.id}>
                                <Text style={styles.categoryText}>{decode(category.name)}</Text>
                                { (index+1 < categories.length ) && <Text style={styles.categoryText}>, </Text> }
                            </View>
                            
                        )
                    })
                }
            </View>
        )
    }

    renderTags = (product) => {
        const { tags } = product
        if(tags.length == 0 ) return null
        return (
            <View style={styles.tagList}>
                <View style={styles.categoryLabel} >
                    { tags.length == 1 && <Text style={styles.categoryLabelText}>{I18n.t('Tag')}:</Text> }
                    { tags.length > 1 && <Text style={styles.categoryLabelText}>{I18n.t('Tags')}:</Text> }
                </View>
                {
                    tags.map((category, index) => {
                        return(
                            <View style={styles.singleCategory} key={category.id}>
                                <Text style={styles.categoryText}>{decode(category.name)}</Text>
                                { (index+1 < tags.length ) && <Text style={styles.categoryText}>, </Text> }
                            </View>
                            
                        )
                    })
                }
            </View>
        )
    }

    renderSku = (product) => {
        const { sku } = product
        if( !sku ) return null
        return (
            <View style={styles.tagList}>
                <View style={styles.categoryLabel} >
                    <Text style={styles.categoryLabelText}>{I18n.t('SKU')}:</Text>
                </View>
                <View style={styles.singleCategory} >
                    <Text style={styles.categoryText}>{sku}</Text>
                </View>
            </View>
        )
    }

    renderAdditionalInfo = (product) => {
        return (
            <View>
                { product.weight != "" && <View style={styles.tagList}>
                    <View style={styles.categoryLabel} >
                        <Text style={styles.categoryLabelText}>{I18n.t('Weight')}:</Text>
                    </View>
                    <View style={styles.singleCategory} >
                        <Text style={styles.categoryText}>{product.weight + ' ' + product.product_units.weight_unit}</Text>
                    </View>
                </View>}
                { ( product.dimensions.length != "" || product.dimensions.width != "" || product.dimensions.height != "" ) && 
                <View style={styles.tagList}>
                    <View style={styles.categoryLabel} >
                        <Text style={styles.categoryLabelText}>{I18n.t('Dimensions')}:</Text>
                    </View>
                    <View style={styles.singleCategory} >
                        <Text style={styles.categoryText}>{product.dimensions.length + ' x ' + product.dimensions.width + ' x ' + product.dimensions.height + ' ' + product.product_units.dimension_unit}</Text>
                    </View>
                </View> }
                { product.attributes.length != 0 && 
                    product.attributes.map((attribute, index) => {
                        if(!attribute.visible ) return null
                        return(
                            <View style={styles.tagList} key={attribute.id}>
                                <View style={styles.categoryLabel} >
                                    <Text style={styles.categoryLabelText}>{attribute.name}:</Text>
                                </View>
                                <View style={styles.singleCategory}>
                                    <Text style={styles.categoryText}>{attribute.options.join(', ')}</Text>
                                </View>
                            </View>
                        )
                    })
                }
            </View>
        )
    }
    renderPolicyData = (policyData) => {
        return (
            <View>
                <View>
                    <H3 style={{textDecorationLine: 'underline', fontWeight: 'bold'}}>{policyData.shipping_policy_heading}</H3>
                    <View>
                        <HtmlViewer
                            html={policyData.shipping_policy}
                            containerStyle={{ padding: 0, paddingRight: 5 }}
                            htmlWrapCssString={`color: ${Colors.text}; font-size: 14px;`}
                        />
                    </View>
                </View>
                <View>
                    <H3 style={{textDecorationLine: 'underline', fontWeight: 'bold'}}>{policyData.refund_policy_heading}</H3>
                    <View>
                        <HtmlViewer
                            html={policyData.refund_policy}
                            containerStyle={{ padding: 0, paddingRight: 5 }}
                            htmlWrapCssString={`color: ${Colors.text}; font-size: 14px;`}
                        />
                    </View>
                </View>
                <View>
                    <H3 style={{textDecorationLine: 'underline', fontWeight: 'bold'}}>{policyData.cancellation_policy_heading}</H3>
                    <View>
                        <HtmlViewer
                            html={policyData.cancellation_policy}
                            containerStyle={{ padding: 0, paddingRight: 5 }}
                            htmlWrapCssString={`color: ${Colors.text}; font-size: 14px;`}
                        />
                    </View>
                </View>
            </View>
        )
    }

    

    render() {
        const { product, isLoading, attributes, variations, isLoadingVariations, variationAttributes, validVariationAttributes } = this.props

        // console.log(validVariationAttributes)

        if (!product || isLoading) {
            return (
                <View>
                    <ActivityIndicator animating size='large' />
                </View>
            )
        }

        if( product.type === 'variable' && ( ! attributes || ! variations || !validVariationAttributes ) ) {
            return (
                <View>
                    <ActivityIndicator animating size='large' />
                </View>
            )
        }

        const gallerySlider = [product.illustration, ...product.gallery]
        const { name: productName, price_html: productPrice, hidePrice, store: productStore, description,
            showAdditionalInfoTab, wcfm_product_policy_data: policyData,
            average_rating: productRating
        } = product

        const scrollY = Animated.add(
            this.state.scrollY,
            Platform.OS === 'ios' ? HEADER_MAX_HEIGHT : 0,
        );

        const headerTranslate = scrollY.interpolate({
            inputRange: [0, HEADER_SCROLL_DISTANCE],
            outputRange: [0, -HEADER_SCROLL_DISTANCE],
            extrapolate: 'clamp',
        });
        const backButtonTranslate = scrollY.interpolate({
            inputRange: [0, HEADER_SCROLL_DISTANCE],
            outputRange: [0, HEADER_SCROLL_DISTANCE -10],
            extrapolate: 'clamp',
        });
        const imageOpacity = scrollY.interpolate({
            inputRange: [0, HEADER_SCROLL_DISTANCE / 4, HEADER_SCROLL_DISTANCE],
            outputRange: [1, 1, 0],
            extrapolate: 'clamp',
        });
        const imageTranslate = scrollY.interpolate({
            inputRange: [0, HEADER_SCROLL_DISTANCE],
            outputRange: [0, 100],
            extrapolate: 'clamp',
        });

        return (
            <View style={styles.fill}>
                <StatusBar barStyle={Platform.OS === 'ios' ? "dark-content" : "light-content"} backgroundColor={Colors.statusBar} />
                <Animated.ScrollView
                    style={styles.fill}
                    scrollEventThrottle={16}
                    onScroll={Animated.event(
                        [{ nativeEvent: { contentOffset: { y: this.state.scrollY } } }],
                        { useNativeDriver: true },
                    )}
                    contentInset={{
                        top: HEADER_MAX_HEIGHT,
                    }}
                    contentOffset={{
                        y: -HEADER_MAX_HEIGHT,
                    }}
                >
                    <View style={styles.scrollViewContent}>
                        {/* Product Title Price */}
                        <View style={styles.productNamePriceSection}>
                            <View style={styles.productNameContainer}>
                                <Text style={styles.productName}>{decode(productName)}</Text>
                            </View>
                            {
                                !Boolean(hidePrice) &&
                                <HtmlViewer
                                    html={productPrice}
                                    containerStyle={{ padding: 0, paddingRight: 5 }}
                                    htmlWrapCssString={`color: ${Colors.text}; font-size: 22px;`}
                                />
                            }
                            {
                                Boolean(productRating) &&
                                <View style={styles.productRating}>
                                    {this.renderRating(productRating)}
                                </View>
                            }
                            
                        </View>
                        {/* Product Title Price End */}
                        {/* Add To Cart Section */}
                        <View style={styles.addToCartSection}>

                            {(product.type === 'variable') && 
                                <View style={styles.variationSection}>
                                    <Form>
                                        {Object.keys(validVariationAttributes).length > 0 && attributes.map((attribute, index) => {
                                            if ( attribute.variation ) {
                                                return (
                                                    <View key={index} style={{ paddingBottom: 20 }}>
                                                        <Text>{attribute.name}</Text>
                                                        <View>
                                                            <Field name={attribute.slug} mode="dialog" style={{ left: 10 }} component={this.renderSelect} >
                                                                <Item key={''} label={I18n.t('Choose an option')} value={''} />
                                                                {validVariationAttributes[attribute.slug].map((option, key) => {
                                                                    return (
                                                                        <Item key={key} label={option} value={option} />
                                                                    )
                                                                })}
                                                            </Field>
                                                        </View>
                                                    </View>
                                                )
                                            } else {
                                                return null
                                            }
                                        })}
                                    </Form>
                                </View>
                            }
                            {
                                this.state.variation_id && this.state.variation_price && 
                                <HtmlViewer
                                    html={this.state.variation_price_html}
                                    containerStyle={{ padding: 0, paddingRight: 5 }}
                                    htmlWrapCssString={`color: ${Colors.text}; font-size: 22px;`}
                                />
                            }
                            {   this.renderAddToCart(product)   }
                            {(product.type === 'external') && this.renderEnternalProductButton(product) }

                        </View>
                        {/* Add To Cart Section End */}
                        { this.renderCategories(product) }
                        { this.renderTags(product) }
                        { this.renderSku(product) }
                        <View>
                            <Accordian 
                                    title = {'Description'}
                                    data = {description}
                                    htmlContent = {true}
                            />
                            {
                                !productStore.errors &&
                                <Accordian 
                    title = {'Store'}
                    >
                                    {/* Vendor Section */}
                                    <View style={styles.storeVendorContainer}>
                                        <View style={styles.vendorImageContainer}>
                                            <Image style={styles.vendorImage} small source={{ uri: productStore.vendor_shop_logo }} />
                                        </View>
                                        <View>
                                            <Text>{decode(productStore.vendor_shop_name)}</Text>
                                        </View>
                                    </View>
                                    {/* Vendor Section End */}
                                </Accordian> 
                            }
                            {
                                showAdditionalInfoTab &&
                                <Accordian 
                    title = {'Additional Information'}
                    >
                                    {this.renderAdditionalInfo(product)}
                                </Accordian>
                            }
                            {
                                policyData.visible && 
                                <Accordian 
                    title = {policyData.tab_title}
                    >
                                    {this.renderPolicyData(policyData)}
                                </Accordian>
                            }
                        </View>
                    </View>
                </Animated.ScrollView>
                <Animated.View
                    style={[
                        styles.header,
                        { transform: [{ translateY: headerTranslate }] },
                    ]}
                >
                    <Animated.View 
                        style={[styles.backButtonContainer,
                            { transform: [{ translateY: backButtonTranslate }] },
                        ]}>
                        <TouchableOpacity onPress={() => this.props.navigation.goBack()} style={{flex:1, justifyContent: 'center', alignItems: 'center'}}>
                            <Icon style={{color: Colors.text, marginTop:4, marginRight:4}} name='arrow-back' />
                        </TouchableOpacity>
                    </Animated.View>
                    <Animated.View
                        style={[
                            styles.backgroundImage,
                            {
                                opacity: imageOpacity,
                                transform: [{ translateY: imageTranslate }],
                            },
                        ]}
                    >
                        <AnimatedSlidebox
                            images={gallerySlider}
                            sliderBoxHeight={300}
                            //onCurrentImagePressed={index => console.warn(`image ${index} pressed`)}
                            dotColor={Colors.whiteBackground}
                            inactiveDotColor="#90A4AE"
                            resizeMethod={'resize'}
                            resizeMode={'cover'}
                            dotStyle={styles.sliderNavigationDots}
                        />
                    </Animated.View>
                </Animated.View>
            </View>
        )
    }
}

const mapStateToProps = (state) => {
    return {
        product: ProductSingleSelectors.getProduct(state),
        isLoading: ProductSingleSelectors.isLoading(state),

        attributes: ProductSingleSelectors.getAttributes(state),
        variations: ProductSingleSelectors.getVariations(state),
        isLoadingVariations: ProductSingleSelectors.isLoadingVariations(state),

        variationAttributes: ProductSingleSelectors.getVariationAttributes(state),
        validVariationAttributes: ProductSingleSelectors.getValidVariationAttributes(state),

        getQtyVal: ProductSingleSelectors.getQuantity(state),
        isAddingToCart: CartSelectors.isLoading(state),
    }
}

const mapDispatchToProps = {
    getProduct: ProductSingleActions.productRequest,
    getVariations: ProductSingleActions.variationRequest,
    increaseQty: ProductSingleActions.increaseQty,
    decreaseQty: ProductSingleActions.decreaseQty,
    addToCart: CartActions.addToCartRequest,
    reset: ProductSingleActions.reset,
}

ProductSingleScreen = compose(
    connect(mapStateToProps, mapDispatchToProps),
    withNavigation
)(ProductSingleScreen);

export default reduxForm({
    form: 'variationForm'
})(ProductSingleScreen)

// export default compose(
//   connect(mapStateToProps, mapDispatchToProps),
//   withNavigation
// )(ProductSingleScreen)

This is the Product Display Page it handles the display of products inside the app and is used for every product queried over the rest api connection with Woocommerce. It handles product options etc. see screenshot. [Product Display Page][1] [1]: https://i.stack.imgur.com/1jLXG.png

Where we are stuck is trying to get this button to behave dynamically based on the product being viewed and when Add To Cart is pressed opens the url of the product being viewed in the app in the default browser installed on the device.

We have tried onPress={() => Linking.openURL('https://www.ourwebsite.com/')} which makes every product open to the url set above.

1 Answers1

0

you can use Linking like this:

const loadInBrowser = ( url ) => {
        
        url && Linking.openURL( url ).catch( err => console.log( `Couldn't load page ${err}` ))
    };

first we check if "url" is not null, then we try to openURL with Linking if we got an error we log it into the console.

then you can call this function in a Button or TouchableOpacity like this

    <TouchableOpacity onpress={ loadInBrower } ></TouchableOpacity>
    
<Button
      onPress={ loadInBrower }
      title="open Browser"
      color="#841584"
    />
Jun De Leon
  • 331
  • 3
  • 11
  • This will pop the browser if I understand correctly but wont get the url dynamically, how do i make this button when pressed get the product url being viewed and open on there phones browser. `` – David Desilets May 06 '22 at 14:54