Skip to content
This repository has been archived by the owner on Jun 28, 2021. It is now read-only.

Commit

Permalink
Merge pull request #508 from naveed-ahmad/app_install_banner
Browse files Browse the repository at this point in the history
App install banner #427
  • Loading branch information
thabti authored Dec 8, 2016
2 parents f396ff5 + 8e5fbef commit bc4775f
Show file tree
Hide file tree
Showing 53 changed files with 617 additions and 7 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ tests/functional/output/*
test/functional/screenshots/*
.ssh
webpack-stats.debug.json
*.DS_Store
2 changes: 1 addition & 1 deletion src/components/Ayah/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export default class Ayah extends Component {
renderMedia() {
const { ayah, mediaActions } = this.props;

if (!ayah.mediaContent.length) return false;
if (!!ayah.mediaContent) return false;

return (
<div>
Expand Down
183 changes: 183 additions & 0 deletions src/components/SmartBanner/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import React, { Component, PropTypes } from 'react';
import useragent from 'express-useragent';
import cookie from 'react-cookie';

class SmartBanner extends Component {
static propTypes = {
daysHidden: PropTypes.number,
daysReminder: PropTypes.number,
appStoreLanguage: PropTypes.string,
button: PropTypes.string,
storeText: PropTypes.objectOf(PropTypes.string),
price: PropTypes.objectOf(PropTypes.string),
force: PropTypes.string,
title: PropTypes.string,
author: PropTypes.string,
};

static defaultProps = {
daysHidden: 15,
daysReminder: 90,
appStoreLanguage: 'us',
button: 'View',
storeText: {
ios: 'On the App Store',
android: 'In Google Play',
windows: 'In Windows Store',
kindle: 'In the Amazon Appstore',
},
price: {
ios: 'Free',
android: 'Free',
windows: 'Free',
kindle: 'Free',
},
force: '',
title: '',
author: '',
};

state = {
settings: {},
deviceType: '',
appId: ''
};

setSettings(forceDeviceType) {
const agent = useragent.parse(window.navigator.userAgent);
let deviceType = '';
const osVersion = parseInt(agent.version, 10);

if (forceDeviceType) {
deviceType = forceDeviceType;
} else if ((agent.isAndroid || agent.isAndroidTablet) && (agent.isChrome ? osVersion < 44 : true)) {
deviceType = 'android';
} else if ((agent.isiPad || agent.isiPhone) && (agent.isSafari ? osVersion < 6 : true)) {
deviceType = 'ios';
}

this.setState({deviceType: deviceType});
if (deviceType) {
this.setSettingsForDevice(deviceType);
}
}

parseAppId(metaName) {
const meta = window.document.querySelector(`meta[name="${metaName}"]`);
return /app-id=([^\s,]+)/.exec(meta.getAttribute('content'))[1];;
}

setSettingsForDevice(deviceType) {
const mixins = {
ios: {
icon: 'app-banner-ios.jpg',
appMeta: 'google-play-app',
getStoreLink: () =>
`https://itunes.apple.com/${this.props.appStoreLanguage}/app/id`,
},
android: {
icon: 'app-banner-android.png',
appMeta: 'apple-itunes-app',
getStoreLink: () =>
'http://play.google.com/store/apps/details?id=',
}
};

if (mixins[deviceType]) {
this.setState({
settings: mixins[deviceType],
appId: this.parseAppId(mixins[deviceType].appMeta)
});
}
}

hide() {
window.document.querySelector('html').classList.remove('smartbanner-show');
}

show() {
window.document.querySelector('html').classList.add('smartbanner-show');
}

close() {
this.hide();
cookie.save('smartbanner-closed', 'true', {
path: '/',
expires: +new Date() + this.props.daysHidden * 1000 * 60 * 60 * 24,
});
}

install() {
this.hide();
cookie.save('smartbanner-installed', 'true', {
path: '/',
expires: +new Date() + this.props.daysReminder * 1000 * 60 * 60 * 24,
});
}

retrieveInfo() {
const link = this.state.settings.getStoreLink() + this.state.appId;
const inStore = `
${this.props.price[this.state.deviceType]} - ${this.props.storeText[this.state.deviceType]}`;
const icon = require(`../../../static/images/${this.state.settings.icon}`);

return {
icon,
link,
inStore,
};
}

componentDidMount() {
if (__CLIENT__) {
this.setSettings(this.props.force);
}
}

render() {
// Don't show banner when:
// 1) if device isn't iOS or Android
// 2) website is loaded in app,
// 3) user dismissed banner,
// 4) or we have no app id in meta

if (!this.state.deviceType
|| window.navigator.standalone
|| cookie.load('smartbanner-closed')
|| cookie.load('smartbanner-installed')) {
return null;
}

if (!this.state.appId) {
return null;
}

this.show();

const { icon, link, inStore } = this.retrieveInfo();
const wrapperClassName = `smartbanner smartbanner-${this.state.deviceType}`;
const iconStyle = {
backgroundImage: `url(${icon})`,
};

return (
<div className={wrapperClassName}>
<div className="smartbanner-container">
<a className="smartbanner-close" onClick={::this.close} data-metrics-event-name="SmartBanner:close"><i className="fa fa-times-circle"></i></a>
<span className="smartbanner-icon" style={iconStyle}></span>
<div className="smartbanner-info">
<div className="smartbanner-title">{this.props.title}</div>
<div>{this.props.author}</div>
<span>{inStore}</span>
</div>

<a href={link} onClick={::this.install} className="smartbanner-button" data-metrics-event-name="SmartBanner:InstallAapp">
<span className="smartbanner-button-text">{this.props.button}</span>
</a>
</div>
</div>
);
}
}

export default SmartBanner;
38 changes: 35 additions & 3 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,43 @@ module.exports = Object.assign({
{name: 'twitter:description', content: description},
{name: 'twitter:image', content: 'https://quran.com/images/thumbnail.png'},
{name: 'twitter:image:width', content: '200'},
{name: 'twitter:image:height', content: '200'}
{name: 'twitter:image:height', content: '200'},
{name: 'google-play-app', content: 'app-id=com.quran.labs.androidquran'},
{name: 'apple-itunes-app', content: 'app-id=1118663303' },
{name: 'mobile-web-app-capable', content: 'yes'},
{name: 'apple-mobile-web-app-capable', content: 'yes'},
{name: 'apple-mobile-web-app-title', content: title},
{name: 'apple-mobile-web-app-status-bar-style', content: 'black'},
{name: 'application-name', content: 'Al-Quran - القرآن الكريم'},
{name: 'msapplication-TileColor', content: '#004f54'},
{name: 'msapplication-tooltip', content: description},
{name: 'msapplication-starturl', content: "https://quran.com"},
{name: 'msapplication-navbutton-color', content: '#004f54'},
{name: 'msapplication-square70x70logo', content: '/mstitle-70x70.jpg'},
{name: 'msapplication-square150x150logo', content: '/mstitle-150x150.jpg'},
{name: 'msapplication-wide310x150logo', content: '/mstitle-310x150.jpg'},
{name: 'msapplication-square310x310logo', content: '/mstitle-310x310.jpg'}
],
link: [
{rel: 'apple-touch-icon', href: '/images/apple-touch-icon.png'},
{rel: 'apple-touch-icon-precomposed', href: '/images/apple-touch-icon-precomposed.png'},
{rel: 'manifest', href: 'manifest.json'},
{rel: 'search', type: 'application/opensearchdescription+xml', href: '/opensearch.xml', title: 'Quran.com'},
{rel:'fluid-icon', href: '/apple-touch-icon-180x180.png', title: 'Quran.com'},
{rel: 'icon', type: 'image/png', href: '/favicon-32x32.png', sizes:'32x32'},
{rel: 'icon', type: 'image/png', href: '/android-chrome-192x192.png', sizes: '192x192'},
{rel: 'icon', type: 'image/png', href: '/favicon-16x16.png', sizes: '16x16'},
{rel: 'mask-icon', href: '/safari-pinned-tab.svg', color: '#004f54'},
{rel: 'shortcut icon', href: '/favicon.ico', type: 'image/x-icon'},
{rel: 'apple-touch-icon', href: 'apple-touch-icon.png'},
{rel: 'apple-touch-icon', sizes: '57x57', href:' /apple-touch-icon-57x57.png'},
{rel: 'apple-touch-icon', sizes: '72x72', href: '/apple-touch-icon-72x72.png'},
{rel: 'apple-touch-icon', sizes: '76x76', href: '/apple-touch-icon-76x76.png'},
{rel: 'apple-touch-icon', sizes: '114x114', href: '/apple-touch-icon-114x114.png'},
{rel: 'apple-touch-icon', sizes: '120x120', href: '/apple-touch-icon-120x120.png'},
{rel: 'apple-touch-icon', sizes: '144x144', href: '/apple-touch-icon-144x144.png'},
{rel: 'apple-touch-icon', sizes: '152x152', href: '/apple-touch-icon-152x152.png'},
{rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon-180x180.png'},
{rel: 'preconnect', href: 'https://quran-1f14.kxcdn.com', crossorigin: ''},
{rel: 'preconnect', href: 'https://assets-1f14.kxcdn.com', crossorigin: ''},
{rel: 'stylesheet', href: 'https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css'}
],
/* SEO: https://developers.google.com/structured-data/slsb-overview#markup_examples */
Expand Down
3 changes: 3 additions & 0 deletions src/containers/App/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { connect } from 'react-redux';
import { asyncConnect } from 'redux-connect';
import Helmet from 'react-helmet';
import Modal from 'react-bootstrap/lib/Modal';
import SmartBanner from 'components/SmartBanner';

const ModalHeader = Modal.Header;
const ModalTitle = Modal.Title;
const ModalBody = Modal.Body;
Expand Down Expand Up @@ -40,6 +42,7 @@ class App extends Component {
<Helmet {...config.app.head} />
<FontStyles />
{children}
<SmartBanner title="The Noble Quran - القرآن الكريم" button="Install"/>
<Footer />
<Modal bsSize="large" show={!!media.content} onHide={removeMedia}>
<ModalHeader closeButton>
Expand Down
4 changes: 2 additions & 2 deletions src/containers/Surah/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
}

:local .surah-container {
padding-top: 130px;
padding-top: 50px;

@media(max-width: $screen-xs-max) {
padding-top: 100px;
padding-top: 50px;
}
}
6 changes: 6 additions & 0 deletions src/helpers/Html.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ const Html = ({ store, component, assets }) => {
}}
charSet="UTF-8"
/>
<script
dangerouslySetInnerHTML={{
__html: `if ('serviceWorker' in navigator) {navigator.serviceWorker.register('/quran-service-worker.js', {scope: './'}).then(function(registration) {}).catch(function(error) {});}`
}}
charSet="UTF-8"
/>
<script
dangerouslySetInnerHTML={{__html: `window.reduxData=${serialize(store.getState())};`}}
charSet="UTF-8"
Expand Down
2 changes: 1 addition & 1 deletion src/server/config/express.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export default function(server) {
server.use(cors());

// Static content
server.use(favicon(path.join((process.env.PWD || process.env.pm_cwd) , '/static/images/favicon.ico')));
server.use(favicon(path.join((process.env.PWD || process.env.pm_cwd) , '/static/favicon.ico')));
server.use(express.static(path.join(process.env.PWD || process.env.pm_cwd, '/static')));
server.use('/public', express.static(path.join((process.env.PWD || process.env.pm_cwd), '/static/dist')));
// server.use('/build', express.static(path.join((process.env.PWD || process.env.pm_cwd), '/static/dist')));
Expand Down
Loading

0 comments on commit bc4775f

Please sign in to comment.