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

Commit 97761db

Browse files
committed
Merge branch 'master' into fix/loading-surah
2 parents 970a551 + bc4775f commit 97761db

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+617
-7
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ tests/functional/output/*
1515
test/functional/screenshots/*
1616
.ssh
1717
webpack-stats.debug.json
18+
*.DS_Store

src/components/Ayah/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export default class Ayah extends Component {
9999
renderMedia() {
100100
const { ayah, mediaActions } = this.props;
101101

102-
if (!ayah.mediaContent.length) return false;
102+
if (!!ayah.mediaContent) return false;
103103

104104
return (
105105
<div>
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import React, { Component, PropTypes } from 'react';
2+
import useragent from 'express-useragent';
3+
import cookie from 'react-cookie';
4+
5+
class SmartBanner extends Component {
6+
static propTypes = {
7+
daysHidden: PropTypes.number,
8+
daysReminder: PropTypes.number,
9+
appStoreLanguage: PropTypes.string,
10+
button: PropTypes.string,
11+
storeText: PropTypes.objectOf(PropTypes.string),
12+
price: PropTypes.objectOf(PropTypes.string),
13+
force: PropTypes.string,
14+
title: PropTypes.string,
15+
author: PropTypes.string,
16+
};
17+
18+
static defaultProps = {
19+
daysHidden: 15,
20+
daysReminder: 90,
21+
appStoreLanguage: 'us',
22+
button: 'View',
23+
storeText: {
24+
ios: 'On the App Store',
25+
android: 'In Google Play',
26+
windows: 'In Windows Store',
27+
kindle: 'In the Amazon Appstore',
28+
},
29+
price: {
30+
ios: 'Free',
31+
android: 'Free',
32+
windows: 'Free',
33+
kindle: 'Free',
34+
},
35+
force: '',
36+
title: '',
37+
author: '',
38+
};
39+
40+
state = {
41+
settings: {},
42+
deviceType: '',
43+
appId: ''
44+
};
45+
46+
setSettings(forceDeviceType) {
47+
const agent = useragent.parse(window.navigator.userAgent);
48+
let deviceType = '';
49+
const osVersion = parseInt(agent.version, 10);
50+
51+
if (forceDeviceType) {
52+
deviceType = forceDeviceType;
53+
} else if ((agent.isAndroid || agent.isAndroidTablet) && (agent.isChrome ? osVersion < 44 : true)) {
54+
deviceType = 'android';
55+
} else if ((agent.isiPad || agent.isiPhone) && (agent.isSafari ? osVersion < 6 : true)) {
56+
deviceType = 'ios';
57+
}
58+
59+
this.setState({deviceType: deviceType});
60+
if (deviceType) {
61+
this.setSettingsForDevice(deviceType);
62+
}
63+
}
64+
65+
parseAppId(metaName) {
66+
const meta = window.document.querySelector(`meta[name="${metaName}"]`);
67+
return /app-id=([^\s,]+)/.exec(meta.getAttribute('content'))[1];;
68+
}
69+
70+
setSettingsForDevice(deviceType) {
71+
const mixins = {
72+
ios: {
73+
icon: 'app-banner-ios.jpg',
74+
appMeta: 'google-play-app',
75+
getStoreLink: () =>
76+
`https://itunes.apple.com/${this.props.appStoreLanguage}/app/id`,
77+
},
78+
android: {
79+
icon: 'app-banner-android.png',
80+
appMeta: 'apple-itunes-app',
81+
getStoreLink: () =>
82+
'http://play.google.com/store/apps/details?id=',
83+
}
84+
};
85+
86+
if (mixins[deviceType]) {
87+
this.setState({
88+
settings: mixins[deviceType],
89+
appId: this.parseAppId(mixins[deviceType].appMeta)
90+
});
91+
}
92+
}
93+
94+
hide() {
95+
window.document.querySelector('html').classList.remove('smartbanner-show');
96+
}
97+
98+
show() {
99+
window.document.querySelector('html').classList.add('smartbanner-show');
100+
}
101+
102+
close() {
103+
this.hide();
104+
cookie.save('smartbanner-closed', 'true', {
105+
path: '/',
106+
expires: +new Date() + this.props.daysHidden * 1000 * 60 * 60 * 24,
107+
});
108+
}
109+
110+
install() {
111+
this.hide();
112+
cookie.save('smartbanner-installed', 'true', {
113+
path: '/',
114+
expires: +new Date() + this.props.daysReminder * 1000 * 60 * 60 * 24,
115+
});
116+
}
117+
118+
retrieveInfo() {
119+
const link = this.state.settings.getStoreLink() + this.state.appId;
120+
const inStore = `
121+
${this.props.price[this.state.deviceType]} - ${this.props.storeText[this.state.deviceType]}`;
122+
const icon = require(`../../../static/images/${this.state.settings.icon}`);
123+
124+
return {
125+
icon,
126+
link,
127+
inStore,
128+
};
129+
}
130+
131+
componentDidMount() {
132+
if (__CLIENT__) {
133+
this.setSettings(this.props.force);
134+
}
135+
}
136+
137+
render() {
138+
// Don't show banner when:
139+
// 1) if device isn't iOS or Android
140+
// 2) website is loaded in app,
141+
// 3) user dismissed banner,
142+
// 4) or we have no app id in meta
143+
144+
if (!this.state.deviceType
145+
|| window.navigator.standalone
146+
|| cookie.load('smartbanner-closed')
147+
|| cookie.load('smartbanner-installed')) {
148+
return null;
149+
}
150+
151+
if (!this.state.appId) {
152+
return null;
153+
}
154+
155+
this.show();
156+
157+
const { icon, link, inStore } = this.retrieveInfo();
158+
const wrapperClassName = `smartbanner smartbanner-${this.state.deviceType}`;
159+
const iconStyle = {
160+
backgroundImage: `url(${icon})`,
161+
};
162+
163+
return (
164+
<div className={wrapperClassName}>
165+
<div className="smartbanner-container">
166+
<a className="smartbanner-close" onClick={::this.close} data-metrics-event-name="SmartBanner:close"><i className="fa fa-times-circle"></i></a>
167+
<span className="smartbanner-icon" style={iconStyle}></span>
168+
<div className="smartbanner-info">
169+
<div className="smartbanner-title">{this.props.title}</div>
170+
<div>{this.props.author}</div>
171+
<span>{inStore}</span>
172+
</div>
173+
174+
<a href={link} onClick={::this.install} className="smartbanner-button" data-metrics-event-name="SmartBanner:InstallAapp">
175+
<span className="smartbanner-button-text">{this.props.button}</span>
176+
</a>
177+
</div>
178+
</div>
179+
);
180+
}
181+
}
182+
183+
export default SmartBanner;

src/config.js

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,43 @@ module.exports = Object.assign({
4545
{name: 'twitter:description', content: description},
4646
{name: 'twitter:image', content: 'https://quran.com/images/thumbnail.png'},
4747
{name: 'twitter:image:width', content: '200'},
48-
{name: 'twitter:image:height', content: '200'}
48+
{name: 'twitter:image:height', content: '200'},
49+
{name: 'google-play-app', content: 'app-id=com.quran.labs.androidquran'},
50+
{name: 'apple-itunes-app', content: 'app-id=1118663303' },
51+
{name: 'mobile-web-app-capable', content: 'yes'},
52+
{name: 'apple-mobile-web-app-capable', content: 'yes'},
53+
{name: 'apple-mobile-web-app-title', content: title},
54+
{name: 'apple-mobile-web-app-status-bar-style', content: 'black'},
55+
{name: 'application-name', content: 'Al-Quran - القرآن الكريم'},
56+
{name: 'msapplication-TileColor', content: '#004f54'},
57+
{name: 'msapplication-tooltip', content: description},
58+
{name: 'msapplication-starturl', content: "https://quran.com"},
59+
{name: 'msapplication-navbutton-color', content: '#004f54'},
60+
{name: 'msapplication-square70x70logo', content: '/mstitle-70x70.jpg'},
61+
{name: 'msapplication-square150x150logo', content: '/mstitle-150x150.jpg'},
62+
{name: 'msapplication-wide310x150logo', content: '/mstitle-310x150.jpg'},
63+
{name: 'msapplication-square310x310logo', content: '/mstitle-310x310.jpg'}
4964
],
5065
link: [
51-
{rel: 'apple-touch-icon', href: '/images/apple-touch-icon.png'},
52-
{rel: 'apple-touch-icon-precomposed', href: '/images/apple-touch-icon-precomposed.png'},
66+
{rel: 'manifest', href: 'manifest.json'},
67+
{rel: 'search', type: 'application/opensearchdescription+xml', href: '/opensearch.xml', title: 'Quran.com'},
68+
{rel:'fluid-icon', href: '/apple-touch-icon-180x180.png', title: 'Quran.com'},
69+
{rel: 'icon', type: 'image/png', href: '/favicon-32x32.png', sizes:'32x32'},
70+
{rel: 'icon', type: 'image/png', href: '/android-chrome-192x192.png', sizes: '192x192'},
71+
{rel: 'icon', type: 'image/png', href: '/favicon-16x16.png', sizes: '16x16'},
72+
{rel: 'mask-icon', href: '/safari-pinned-tab.svg', color: '#004f54'},
73+
{rel: 'shortcut icon', href: '/favicon.ico', type: 'image/x-icon'},
74+
{rel: 'apple-touch-icon', href: 'apple-touch-icon.png'},
75+
{rel: 'apple-touch-icon', sizes: '57x57', href:' /apple-touch-icon-57x57.png'},
76+
{rel: 'apple-touch-icon', sizes: '72x72', href: '/apple-touch-icon-72x72.png'},
77+
{rel: 'apple-touch-icon', sizes: '76x76', href: '/apple-touch-icon-76x76.png'},
78+
{rel: 'apple-touch-icon', sizes: '114x114', href: '/apple-touch-icon-114x114.png'},
79+
{rel: 'apple-touch-icon', sizes: '120x120', href: '/apple-touch-icon-120x120.png'},
80+
{rel: 'apple-touch-icon', sizes: '144x144', href: '/apple-touch-icon-144x144.png'},
81+
{rel: 'apple-touch-icon', sizes: '152x152', href: '/apple-touch-icon-152x152.png'},
82+
{rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon-180x180.png'},
83+
{rel: 'preconnect', href: 'https://quran-1f14.kxcdn.com', crossorigin: ''},
84+
{rel: 'preconnect', href: 'https://assets-1f14.kxcdn.com', crossorigin: ''},
5385
{rel: 'stylesheet', href: 'https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css'}
5486
],
5587
/* SEO: https://developers.google.com/structured-data/slsb-overview#markup_examples */

src/containers/App/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { connect } from 'react-redux';
55
import { asyncConnect } from 'redux-connect';
66
import Helmet from 'react-helmet';
77
import Modal from 'react-bootstrap/lib/Modal';
8+
import SmartBanner from 'components/SmartBanner';
9+
810
const ModalHeader = Modal.Header;
911
const ModalTitle = Modal.Title;
1012
const ModalBody = Modal.Body;
@@ -40,6 +42,7 @@ class App extends Component {
4042
<Helmet {...config.app.head} />
4143
<FontStyles />
4244
{children}
45+
<SmartBanner title="The Noble Quran - القرآن الكريم" button="Install"/>
4346
<Footer />
4447
<Modal bsSize="large" show={!!media.content} onHide={removeMedia}>
4548
<ModalHeader closeButton>

src/containers/Surah/style.scss

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
}
2828

2929
:local .surah-container {
30-
padding-top: 130px;
30+
padding-top: 50px;
3131

3232
@media(max-width: $screen-xs-max) {
33-
padding-top: 100px;
33+
padding-top: 50px;
3434
}
3535
}

src/helpers/Html.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ const Html = ({ store, component, assets }) => {
4646
}}
4747
charSet="UTF-8"
4848
/>
49+
<script
50+
dangerouslySetInnerHTML={{
51+
__html: `if ('serviceWorker' in navigator) {navigator.serviceWorker.register('/quran-service-worker.js', {scope: './'}).then(function(registration) {}).catch(function(error) {});}`
52+
}}
53+
charSet="UTF-8"
54+
/>
4955
<script
5056
dangerouslySetInnerHTML={{__html: `window.reduxData=${serialize(store.getState())};`}}
5157
charSet="UTF-8"

src/server/config/express.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export default function(server) {
6767
server.use(cors());
6868

6969
// Static content
70-
server.use(favicon(path.join((process.env.PWD || process.env.pm_cwd) , '/static/images/favicon.ico')));
70+
server.use(favicon(path.join((process.env.PWD || process.env.pm_cwd) , '/static/favicon.ico')));
7171
server.use(express.static(path.join(process.env.PWD || process.env.pm_cwd, '/static')));
7272
server.use('/public', express.static(path.join((process.env.PWD || process.env.pm_cwd), '/static/dist')));
7373
// server.use('/build', express.static(path.join((process.env.PWD || process.env.pm_cwd), '/static/dist')));

0 commit comments

Comments
 (0)