From 28554a92fbac8e9bb2d69222681278cd560b490a Mon Sep 17 00:00:00 2001 From: Mohamed El Mahallawy Date: Wed, 18 Jan 2017 22:06:12 -0800 Subject: [PATCH] Quran v2.3.0 (#592) * Fixes #461 (#473) #461 Adding verse number to tooltip * improved aya/sura url param handling * improved code to ayah/surah url handler * used Route from react-router to handle reroute * Added tooltip prop for Ayah component in Search page (#474) * Create LICENSE.md (#477) * added Nafees font for Urdu and Punjabi translation * footer layout improved (#490) * Quick links layout improved. (#491) * footer layout improved * quick links layout improved * Update index.js * Adding margin to quick links section * User authentication and profile (#460) * wip * user stuff * Profile page! * Unneeded calls * PR feedback * go stateless * dup route * oops decorators * Add user bookmarks * Adding media and modal (#493) * Adding media and modal * squeeze a fix * Renav (#495) * surah page layout breakdown fixed * Fix node-http-proxy host in proxy request (#501) node-http-proxy was putting a host header with the current server name, which was thus causing nginx on the receiving end to send the request to the default handler (or the first one). This patch asks node-http-proxy to rewrite the host and port, and also to change the origin of the host header to onequran's url. * Env problems (#494) * Adding media and modal * Fix envs * Moving docker env closer * return dockerfile as is * fixes * give it a try * try again * desparate times * desparate times * host * sidebar * fix * tooltip fix * corrected elements nesting * edited * fixing tooltip bottom triangle glitch * moved getOffset function to utils folder * formatted code * wip on app install banner #427 * used fa icon for close btn * added event tracking for install and close banner * using default fonts for banner * removed one hiding console.log * fix unit tests * tooltip fix * corrected elements nesting * edited * fixing tooltip bottom triangle glitch * moved getOffset function to utils folder * formatted code * removed one hiding console.log * corrected QuickSurah assertion test * corrected quicksurah tests * experimental check * another exprerimental check * another workaround for tests * exporting tooltip code to file * editing. * another attempt * another attempt * removing eslint errors * removing eslint errors - final * removing eslint errors - final * fixed the banner icon and minor styling * do not always load bismillah (#515) * do not always load bismillah * while at it, lets not load images when we do not need them * Code splitting (#516) * Code splitting to work * working * added favicons, opensearch, and manifest files for chrome * removed web from related app * highlight currently playing ayah (#510) * added service worker and preconnect meta tags * don' show custom banner on chrome > 44 and safari > 6 * meh, when this was replaced * added prefer related apps * initial work on react-share * fix style * fix facebook share * add cursor pointer for the icons * fix surah still display Loading even in last of surah (#521) * switched hightlight color to green * added nightmode css and optimized svgs #201 * fixed night color for missing elements, added bare min component * added hightlight for readmore * changed night more UI * updated night mode UI * Added no script warning (#527) * added no script warning * fixed messaging * minor css changes * removed font size * fixed noscript break (#535) * fixed noscript break * fixed the link * loaders when navigating on client side * notes on autocomplete * Use new segments and surah info data (#542) * Use new segments and surah info data * delete alot of files * removed bootstrap scripts and descriptions * Local support (#523) * Work in progress for #245 * refactored local, local specific fonts, almost done with i18n :) * fixed search break and added other missing translation keys * added local switch * fixed the bug * fixed noscript break * added intl pollyfil * replaced double quote with single quote * fixing specs * formatting * more formatting * fixed noscript break * fixed the link * fixed specs using mound instead of shallow * added arabic local * PhantomJS don't support! fixed intl helper with vanilla js * added local for bookmark * Fixed sidebar (#554) * we needed bs dropdown man * fixed sidebar * New React + eslint (#543) * stylelint * autocomplete * make search work * more lints * fix audio * remove row component * move to deps * tests are not running * tests passing * fix travis tests * jquery * Add new normalizr (#565) * (#564) Fixes #562 Highlight ayah when playing * Fixes #557 Scrollbars bouncing (#563) * Add slack to readme (#570) * Fixes #547 Support continuing from last visited ayah (#559) * Fix last visited functionality - Read last visited surah/ayah from cookie and show resume option on main page if cookie exists - Refactor and cleanup previous last visited code to be more readable and match the new UI * Update lastVisit cookie ayah requests, switch cookie ayah field to ayahId for consistency * Delete last visit styles.scss * Change resume heading to continue and support urdu/arabic translations * Move vertical lines from class title into new class The existing .title class made it difficult for titles that are not a series of items to use the span tag as it would add unwanted vertical lines. The only user of this was QuickSurahs component. I moved the vertical lines into a new "items" class so that all other users which are not a series of items can use the title class without getting vertical lines in spans. Only last visit benefits from this at the moment since the main Home component doesn't have any spans inside the title and there are no other users of this scss file. * Add debug line back in lastvisit * Minor refactoring * Devise Token Auth for one quran (#571) * Devise Token Auth for one quran * pr comments * Highlight word in reading mode as well (#556) * highlight current word is readmode as well * added word by word play! buggy but working * fixed word position in reading modet * DRYed word render and added tooltip in reading mode * fixed trans language class * refactoring word rendering * refactored word rendering * removed duplicated code * removed debuging * adding auto scrolling in reading mode * Update docs (#567) * Update docs * Update CONTRIBUTING.md * Update README.md * Mention staging * Optimization of assets (#585) * quick optimization of assets * typo * #568 UI improvements (#583) * moved topoptions to navbar drawer * fix lint * fix tests * break eslint * fix eslint * fix test * verse dropdown max height * Global Nav * small bug * change again * placeholder for search * global nav edits and to merge so one pr * a ton of changes and i want to sleep now * fix test * remove unneeded * more delete * fix errors * fixes #https://github.com/quran/quran.com-frontend/issues/561 * console * delete that * minor style changes * pr comments * fixes audio * always close * Remove missing contents (#589) --- .env | 5 +- .eslintignore | 1 - .eslintrc | 49 +- .gitignore | 1 + CONTRIBUTING.md | 7 - Dockerfile | 4 + LICENSE.md | 21 + README.md | 35 +- karma.conf.js | 6 +- package.json | 75 +- src/client.js | 22 +- .../Audioplayer/RepeatDropdown/index.js | 92 +- .../Audioplayer/RepeatDropdown/spec.js | 26 +- .../Audioplayer/ScrollButton/index.js | 16 +- .../Audioplayer/ScrollButton/spec.js | 14 +- src/components/Audioplayer/Segments/index.js | 7 +- src/components/Audioplayer/Segments/spec.js | 19 +- src/components/Audioplayer/Track/index.js | 14 +- src/components/Audioplayer/Track/spec.js | 7 +- src/components/Audioplayer/Track/style.scss | 17 +- src/components/Audioplayer/index.js | 136 +- src/components/Audioplayer/spec.js | 64 +- src/components/Audioplayer/style.scss | 58 +- src/components/Ayah/index.js | 222 +- src/components/Ayah/spec.js | 7 +- src/components/Ayah/style.scss | 12 +- src/components/Bismillah/index.js | 7 +- src/components/ContentDropdown/index.js | 60 +- src/components/ContentDropdown/spec.js | 31 +- src/components/ContentDropdown/style.scss | 42 +- src/components/Copy/index.js | 12 +- src/components/Copy/spec.js | 3 +- src/components/FacebookButton/index.js | 109 + src/components/FacebookTokenButton/index.js | 34 + src/components/FacebookTokenButton/style.scss | 6 + src/components/FontSizeDropdown/index.js | 75 +- src/components/FontSizeDropdown/style.scss | 8 +- src/components/FontStyles/index.js | 8 +- src/components/FontStyles/selector.js | 2 +- src/components/Footer/index.js | 162 + src/components/Footer/style.scss | 41 + src/components/GlobalNav/Surah/index.js | 80 + src/components/GlobalNav/index.js | 130 + src/components/GlobalNav/style.scss | 20 + src/components/GlobalSidebar/Surah/index.js | 29 + src/components/GlobalSidebar/index.js | 132 + src/components/GlobalSidebar/style.scss | 64 + src/components/Home/LastVisit/index.js | 70 +- src/components/Home/LastVisit/style.scss | 12 - src/components/Home/QuickSurahs/index.js | 44 +- src/components/Home/QuickSurahs/spec.js | 27 +- src/components/Home/SurahsList/index.js | 9 +- src/components/Home/SurahsList/spec.js | 14 +- src/components/IndexHeader/Nav/index.js | 85 - src/components/IndexHeader/index.js | 12 +- src/components/InformationToggle/index.js | 48 +- src/components/LazyLoad/index.js | 11 +- src/components/LazyLoad/spec.js | 9 +- src/components/Line/index.js | 71 +- src/components/Loader/index.js | 11 +- src/components/Loader/style.scss | 49 + .../LocaleFormattedMessage/index.js | 22 + src/components/LocaleSwitcher/index.js | 68 + src/components/LocaleSwitcher/style.scss | 7 + src/components/NightModeToggle/index.js | 34 + src/components/NoScript/index.js | 13 + src/components/NoScript/style.scss | 11 + src/components/PageBreak/index.js | 7 +- src/components/PageBreak/spec.js | 7 +- src/components/Radio/index.js | 30 + src/components/Radio/style.scss | 130 + src/components/ReadingModeToggle/index.js | 28 +- src/components/ReciterDropdown/index.js | 42 +- src/components/SearchAutocomplete/index.js | 136 +- src/components/SearchInput/index.js | 31 +- src/components/SettingsModal/index.js | 86 + src/components/Share/index.js | 54 +- src/components/Share/style.scss | 13 +- src/components/SmartBanner/index.js | 202 + src/components/SurahInfo/htmls/1.html.js | 10 - src/components/SurahInfo/htmls/10.html.js | 42 - src/components/SurahInfo/htmls/100.html.js | 10 - src/components/SurahInfo/htmls/101.html.js | 8 - src/components/SurahInfo/htmls/102.html.js | 14 - src/components/SurahInfo/htmls/103.html.js | 8 - src/components/SurahInfo/htmls/104.html.js | 11 - src/components/SurahInfo/htmls/105.html.js | 38 - src/components/SurahInfo/htmls/106.html.js | 18 - src/components/SurahInfo/htmls/107.html.js | 9 - src/components/SurahInfo/htmls/108.html.js | 19 - src/components/SurahInfo/htmls/109.html.js | 23 - src/components/SurahInfo/htmls/11.html.js | 13 - src/components/SurahInfo/htmls/110.html.js | 23 - src/components/SurahInfo/htmls/111.html.js | 25 - src/components/SurahInfo/htmls/112.html.js | 46 - src/components/SurahInfo/htmls/113.html.js | 91 - src/components/SurahInfo/htmls/114.html.js | 91 - src/components/SurahInfo/htmls/12.html.js | 31 - src/components/SurahInfo/htmls/13.html.js | 11 - src/components/SurahInfo/htmls/14.html.js | 8 - src/components/SurahInfo/htmls/15.html.js | 17 - src/components/SurahInfo/htmls/16.html.js | 43 - src/components/SurahInfo/htmls/17.html.js | 15 - src/components/SurahInfo/htmls/18.html.js | 25 - src/components/SurahInfo/htmls/19.html.js | 26 - src/components/SurahInfo/htmls/2.html.js | 59 - src/components/SurahInfo/htmls/20.html.js | 32 - src/components/SurahInfo/htmls/21.html.js | 39 - src/components/SurahInfo/htmls/22.html.js | 22 - src/components/SurahInfo/htmls/23.html.js | 31 - src/components/SurahInfo/htmls/24.html.js | 120 - src/components/SurahInfo/htmls/25.html.js | 9 - src/components/SurahInfo/htmls/26.html.js | 25 - src/components/SurahInfo/htmls/27.html.js | 15 - src/components/SurahInfo/htmls/28.html.js | 18 - src/components/SurahInfo/htmls/29.html.js | 13 - src/components/SurahInfo/htmls/3.html.js | 41 - src/components/SurahInfo/htmls/30.html.js | 25 - src/components/SurahInfo/htmls/31.html.js | 10 - src/components/SurahInfo/htmls/32.html.js | 15 - src/components/SurahInfo/htmls/33.html.js | 88 - src/components/SurahInfo/htmls/34.html.js | 9 - src/components/SurahInfo/htmls/35.html.js | 12 - src/components/SurahInfo/htmls/36.html.js | 12 - src/components/SurahInfo/htmls/37.html.js | 12 - src/components/SurahInfo/htmls/38.html.js | 19 - src/components/SurahInfo/htmls/39.html.js | 9 - src/components/SurahInfo/htmls/4.html.js | 42 - src/components/SurahInfo/htmls/40.html.js | 28 - src/components/SurahInfo/htmls/41.html.js | 53 - src/components/SurahInfo/htmls/42.html.js | 25 - src/components/SurahInfo/htmls/43.html.js | 18 - src/components/SurahInfo/htmls/44.html.js | 32 - src/components/SurahInfo/htmls/45.html.js | 28 - src/components/SurahInfo/htmls/46.html.js | 22 - src/components/SurahInfo/htmls/47.html.js | 17 - src/components/SurahInfo/htmls/48.html.js | 66 - src/components/SurahInfo/htmls/49.html.js | 15 - src/components/SurahInfo/htmls/5.html.js | 45 - src/components/SurahInfo/htmls/50.html.js | 11 - src/components/SurahInfo/htmls/51.html.js | 13 - src/components/SurahInfo/htmls/52.html.js | 11 - src/components/SurahInfo/htmls/53.html.js | 25 - src/components/SurahInfo/htmls/54.html.js | 13 - src/components/SurahInfo/htmls/55.html.js | 27 - src/components/SurahInfo/htmls/56.html.js | 16 - src/components/SurahInfo/htmls/57.html.js | 42 - src/components/SurahInfo/htmls/58.html.js | 15 - src/components/SurahInfo/htmls/59.html.js | 71 - src/components/SurahInfo/htmls/6.html.js | 58 - src/components/SurahInfo/htmls/60.html.js | 13 - src/components/SurahInfo/htmls/61.html.js | 13 - src/components/SurahInfo/htmls/62.html.js | 24 - src/components/SurahInfo/htmls/63.html.js | 31 - src/components/SurahInfo/htmls/64.html.js | 23 - src/components/SurahInfo/htmls/65.html.js | 34 - src/components/SurahInfo/htmls/66.html.js | 28 - src/components/SurahInfo/htmls/67.html.js | 16 - src/components/SurahInfo/htmls/68.html.js | 14 - src/components/SurahInfo/htmls/69.html.js | 12 - src/components/SurahInfo/htmls/7.html.js | 22 - src/components/SurahInfo/htmls/70.html.js | 12 - src/components/SurahInfo/htmls/71.html.js | 13 - src/components/SurahInfo/htmls/72.html.js | 28 - src/components/SurahInfo/htmls/73.html.js | 17 - src/components/SurahInfo/htmls/74.html.js | 29 - src/components/SurahInfo/htmls/75.html.js | 10 - src/components/SurahInfo/htmls/76.html.js | 23 - src/components/SurahInfo/htmls/77.html.js | 15 - src/components/SurahInfo/htmls/78.html.js | 20 - src/components/SurahInfo/htmls/79.html.js | 15 - src/components/SurahInfo/htmls/8.html.js | 82 - src/components/SurahInfo/htmls/80.html.js | 15 - src/components/SurahInfo/htmls/81.html.js | 10 - src/components/SurahInfo/htmls/82.html.js | 10 - src/components/SurahInfo/htmls/83.html.js | 12 - src/components/SurahInfo/htmls/84.html.js | 12 - src/components/SurahInfo/htmls/85.html.js | 10 - src/components/SurahInfo/htmls/86.html.js | 10 - src/components/SurahInfo/htmls/87.html.js | 14 - src/components/SurahInfo/htmls/88.html.js | 12 - src/components/SurahInfo/htmls/89.html.js | 13 - src/components/SurahInfo/htmls/9.html.js | 90 - src/components/SurahInfo/htmls/90.html.js | 13 - src/components/SurahInfo/htmls/91.html.js | 23 - src/components/SurahInfo/htmls/92.html.js | 13 - src/components/SurahInfo/htmls/93.html.js | 13 - src/components/SurahInfo/htmls/94.html.js | 12 - src/components/SurahInfo/htmls/95.html.js | 11 - src/components/SurahInfo/htmls/96.html.js | 22 - src/components/SurahInfo/htmls/97.html.js | 12 - src/components/SurahInfo/htmls/98.html.js | 12 - src/components/SurahInfo/htmls/99.html.js | 11 - src/components/SurahInfo/htmls/README.md | 13 - src/components/SurahInfo/htmls/index.js | 116 - src/components/SurahInfo/index.js | 26 +- src/components/SurahInfo/style.scss | 2 +- src/components/SurahsDropdown/index.js | 45 +- src/components/SurahsDropdown/style.scss | 7 +- src/components/SwitchToggle/index.js | 2 +- src/components/TooltipDropdown/index.js | 89 +- src/components/TopOptions/index.js | 53 +- src/components/TopOptions/spec.js | 40 +- src/components/VersesDropdown/index.js | 35 +- src/components/VersesDropdown/style.scss | 4 +- src/components/Word/index.js | 71 + src/config.js | 97 +- src/containers/About/index.js | 36 +- src/containers/App/connect.js | 12 + src/containers/App/index.js | 170 +- src/containers/App/style.scss | 47 - src/containers/Contact/index.js | 35 +- src/containers/Donations/index.js | 32 +- src/containers/Error/index.js | 2 +- src/containers/Home/index.js | 28 +- src/containers/Home/style.scss | 33 +- src/containers/Login/index.js | 28 + src/containers/MobileLanding/index.js | 13 +- src/containers/Profile/index.js | 69 + src/containers/Profile/style.scss | 52 + src/containers/Search/Header/index.js | 4 +- src/containers/Search/index.js | 99 +- src/containers/Search/style.scss | 2 +- src/containers/Surah/Header/index.js | 92 +- src/containers/Surah/Header/style.scss | 15 + src/containers/Surah/Title/index.js | 81 +- src/containers/Surah/Title/spec.js | 43 +- src/containers/Surah/Title/style.scss | 14 +- src/containers/Surah/connect.js | 26 +- src/containers/Surah/descriptions.js | 117 - src/containers/Surah/index.js | 262 +- src/containers/Surah/style.scss | 38 +- src/helpers/ApiClient.js | 37 +- src/helpers/Html.js | 22 +- src/helpers/buildAudio.js | 7 +- src/helpers/buildFontFaces.js | 6 +- src/helpers/buildSegments.js | 39 +- src/helpers/makeHeadTags.js | 20 +- src/helpers/metrics.js | 3 +- src/helpers/setLocal.js | 30 + src/locale/ar.js | 78 + src/locale/en.js | 78 + src/locale/ur.js | 78 + src/redux/actions/audioplayer.js | 10 +- src/redux/actions/auth.js | 44 + src/redux/actions/ayahs.js | 13 +- src/redux/actions/bookmarks.js | 44 + src/redux/actions/fontFace.js | 12 +- src/redux/actions/media.js | 13 + src/redux/actions/options.js | 10 +- src/redux/actions/search.js | 10 +- src/redux/actions/spec.js | 21 +- src/redux/actions/suggest.js | 17 + src/redux/actions/surahs.js | 35 +- src/redux/constants/audioplayer.js | 2 + src/redux/constants/auth.js | 8 + src/redux/constants/bookmarks.js | 9 + src/redux/constants/fontFace.js | 4 +- src/redux/constants/media.js | 3 + src/redux/constants/options.js | 2 +- src/redux/constants/suggest.js | 3 + src/redux/create.js | 3 +- src/redux/middleware/clientMiddleware.js | 15 +- src/redux/modules/audioplayer.js | 95 +- src/redux/modules/auth.js | 65 + src/redux/modules/ayahs.js | 24 +- src/redux/modules/bookmarks.js | 50 + src/redux/modules/fontFaces.js | 12 +- src/redux/modules/lines.js | 13 +- src/redux/modules/media.js | 27 + src/redux/modules/options.js | 10 +- src/redux/modules/reducer.js | 8 + src/redux/modules/searchResults.js | 2 +- src/redux/modules/suggestResults.js | 39 + src/redux/modules/surahs.js | 28 +- src/redux/schemas.js | 10 +- src/routes.js | 87 +- src/server.js | 109 +- src/server/config/express.js | 53 +- src/server/config/sitemap.js | 11 +- src/server/config/support.js | 2 +- src/styles/bootstrap.config.js | 14 +- src/styles/components/MasterHeader.scss | 111 +- src/styles/components/SmartBanner.scss | 283 + src/styles/components/SurahNav.scss | 42 - src/styles/fonts/_fonts.scss | 779 +- src/styles/main.scss | 87 +- src/styles/mixins/_center.scss | 17 - src/styles/nightmode.scss | 24 + src/styles/partials/_dropdown.scss | 25 - src/styles/partials/_index-header.scss | 1 + src/styles/partials/_modal.scss | 9 - src/styles/partials/_no-script.scss | 11 + src/styles/partials/_search-input.scss | 14 +- src/styles/partials/_surah-title.scss | 99 - src/styles/partials/_tooltip.scss | 72 +- src/styles/variables.scss | 21 +- src/types/ayahType.js | 19 + src/types/bookmarkType.js | 8 + src/types/index.js | 8 + src/types/matchType.js | 26 + src/types/optionsType.js | 14 + src/types/segmentType.js | 13 + src/types/surahType.js | 21 + src/types/userType.js | 13 + src/types/wordType.js | 18 + src/utils/bindTooltip.js | 73 + src/utils/checkValidSurah.js | 12 + src/utils/getOffset.js | 15 + src/utils/scroller.js | 3 +- static/android-chrome-144x144.png | Bin 0 -> 31224 bytes static/android-chrome-192x192.png | Bin 0 -> 48730 bytes static/android-chrome-256x256.png | Bin 0 -> 75861 bytes static/android-chrome-48x48.png | Bin 0 -> 5736 bytes .../apple-touch-icon-114x114-precomposed.png | Bin 0 -> 12663 bytes static/apple-touch-icon-114x114.png | Bin 0 -> 10856 bytes .../apple-touch-icon-120x120-precomposed.png | Bin 0 -> 13852 bytes static/apple-touch-icon-120x120.png | Bin 0 -> 11846 bytes .../apple-touch-icon-144x144-precomposed.png | Bin 0 -> 19147 bytes static/apple-touch-icon-144x144.png | Bin 0 -> 16332 bytes .../apple-touch-icon-152x152-precomposed.png | Bin 0 -> 21076 bytes static/apple-touch-icon-152x152.png | Bin 0 -> 18027 bytes static/apple-touch-icon-57x57-precomposed.png | Bin 0 -> 3922 bytes static/apple-touch-icon-57x57.png | Bin 0 -> 3326 bytes static/apple-touch-icon-60x60-precomposed.png | Bin 0 -> 4255 bytes static/apple-touch-icon-60x60.png | Bin 0 -> 3629 bytes static/apple-touch-icon-72x72-precomposed.png | Bin 0 -> 5786 bytes static/apple-touch-icon-72x72.png | Bin 0 -> 4910 bytes static/apple-touch-icon-76x76-precomposed.png | Bin 0 -> 6338 bytes static/apple-touch-icon-76x76.png | Bin 0 -> 5387 bytes static/apple-touch-icon-precomposed.png | Bin 0 -> 21076 bytes static/apple-touch-icon.png | Bin 0 -> 18027 bytes static/favicon-16x16.png | Bin 0 -> 1067 bytes static/favicon-32x32.png | Bin 0 -> 2046 bytes static/{images => }/favicon.ico | Bin .../fonts/nafees/nafees-nastaleeq-webfont.eot | Bin 0 -> 166290 bytes .../fonts/nafees/nafees-nastaleeq-webfont.svg | 1036 ++ .../fonts/nafees/nafees-nastaleeq-webfont.ttf | Bin 0 -> 364944 bytes .../nafees/nafees-nastaleeq-webfont.woff | Bin 0 -> 191784 bytes static/fonts/ss-standard/documentation.html | 4 +- static/fonts/ss-standard/ss-standard.css | 784 ++ static/images/app-banner-android.png | Bin 0 -> 92818 bytes static/images/app-banner-ios.jpg | Bin 0 -> 5835 bytes .../apple-touch-icon-120x120-precomposed.png | Bin 1150 -> 0 bytes ...apple-touch-icon-120x120-precompressed.png | Bin 1150 -> 0 bytes .../apple-touch-icon-152x152-precomposed.png | Bin 1150 -> 0 bytes static/images/apple-touch-icon-152x152.png | Bin 1150 -> 0 bytes .../apple-touch-icon-57x57-precomposed.png | Bin 1150 -> 0 bytes static/images/apple-touch-icon-57x57.png | Bin 1150 -> 0 bytes .../images/apple-touch-icon-precomposed.png | Bin 1150 -> 0 bytes static/images/apple-touch-icon.png | Bin 1150 -> 0 bytes static/images/titles/001.svg | 179 +- static/images/titles/002.svg | 204 +- static/images/titles/003.svg | 208 +- static/images/titles/004.svg | 206 +- static/images/titles/005.svg | 204 +- static/images/titles/006.svg | 187 +- static/images/titles/007.svg | 217 +- static/images/titles/008.svg | 203 +- static/images/titles/009.svg | 182 +- static/images/titles/010.svg | 179 +- static/images/titles/011.svg | 162 +- static/images/titles/012.svg | 203 +- static/images/titles/013.svg | 159 +- static/images/titles/014.svg | 202 +- static/images/titles/015.svg | 199 +- static/images/titles/016.svg | 180 +- static/images/titles/017.svg | 184 +- static/images/titles/018.svg | 205 +- static/images/titles/019.svg | 151 +- static/images/titles/020.svg | 153 +- static/images/titles/021.svg | 197 +- static/images/titles/022.svg | 163 +- static/images/titles/023.svg | 235 +- static/images/titles/024.svg | 175 +- static/images/titles/025.svg | 193 +- static/images/titles/026.svg | 234 +- static/images/titles/027.svg | 183 +- static/images/titles/028.svg | 204 +- static/images/titles/029.svg | 252 +- static/images/titles/030.svg | 185 +- static/images/titles/031.svg | 186 +- static/images/titles/032.svg | 192 +- static/images/titles/033.svg | 210 +- static/images/titles/034.svg | 179 +- static/images/titles/035.svg | 165 +- static/images/titles/036.svg | 170 +- static/images/titles/037.svg | 208 +- static/images/titles/038.svg | 152 +- static/images/titles/039.svg | 170 +- static/images/titles/040.svg | 178 +- static/images/titles/041.svg | 219 +- static/images/titles/042.svg | 224 +- static/images/titles/043.svg | 212 +- static/images/titles/044.svg | 199 +- static/images/titles/045.svg | 197 +- static/images/titles/046.svg | 224 +- static/images/titles/047.svg | 181 +- static/images/titles/048.svg | 187 +- static/images/titles/049.svg | 248 +- static/images/titles/050.svg | 139 +- static/images/titles/051.svg | 212 +- static/images/titles/052.svg | 194 +- static/images/titles/053.svg | 186 +- static/images/titles/054.svg | 181 +- static/images/titles/055.svg | 195 +- static/images/titles/056.svg | 202 +- static/images/titles/057.svg | 203 +- static/images/titles/058.svg | 224 +- static/images/titles/059.svg | 187 +- static/images/titles/060.svg | 243 +- static/images/titles/061.svg | 198 +- static/images/titles/062.svg | 205 +- static/images/titles/063.svg | 257 +- static/images/titles/064.svg | 226 +- static/images/titles/065.svg | 218 +- static/images/titles/066.svg | 202 +- static/images/titles/067.svg | 187 +- static/images/titles/068.svg | 180 +- static/images/titles/069.svg | 206 +- static/images/titles/070.svg | 177 +- static/images/titles/071.svg | 163 +- static/images/titles/072.svg | 174 +- static/images/titles/073.svg | 211 +- static/images/titles/074.svg | 189 +- static/images/titles/075.svg | 188 +- static/images/titles/076.svg | 215 +- static/images/titles/077.svg | 229 +- static/images/titles/078.svg | 181 +- static/images/titles/079.svg | 232 +- static/images/titles/080.svg | 189 +- static/images/titles/081.svg | 210 +- static/images/titles/082.svg | 216 +- static/images/titles/083.svg | 265 +- static/images/titles/084.svg | 238 +- static/images/titles/085.svg | 201 +- static/images/titles/086.svg | 200 +- static/images/titles/087.svg | 184 +- static/images/titles/088.svg | 243 +- static/images/titles/089.svg | 203 +- static/images/titles/090.svg | 166 +- static/images/titles/091.svg | 242 +- static/images/titles/092.svg | 172 +- static/images/titles/093.svg | 202 +- static/images/titles/094.svg | 195 +- static/images/titles/095.svg | 189 +- static/images/titles/096.svg | 193 +- static/images/titles/097.svg | 173 +- static/images/titles/098.svg | 204 +- static/images/titles/099.svg | 203 +- static/images/titles/100.svg | 239 +- static/images/titles/101.svg | 198 +- static/images/titles/102.svg | 220 +- static/images/titles/103.svg | 215 +- static/images/titles/104.svg | 195 +- static/images/titles/105.svg | 187 +- static/images/titles/106.svg | 210 +- static/images/titles/107.svg | 214 +- static/images/titles/108.svg | 197 +- static/images/titles/109.svg | 223 +- static/images/titles/110.svg | 190 +- static/images/titles/111.svg | 198 +- static/images/titles/112.svg | 228 +- static/images/titles/113.svg | 177 +- static/images/titles/114.svg | 203 +- static/manifest.json | 41 + static/mstitle-150x150.jpg | Bin 0 -> 6288 bytes static/mstitle-310x150.jpg | Bin 0 -> 8462 bytes static/mstitle-310x310.jpg | Bin 0 -> 9345 bytes static/mstitle-70x70.jpg | Bin 0 -> 3818 bytes static/opensearch.xml | 9 + static/quran-service-worker.js | 1 + static/safari-pinned-tab.svg | 32 + tests/fixtures/ayah.json | 1 + tests/helpers/intl-enzyme-test-helper.js | 40 + webpack/dev.config.js | 5 +- webpack/prod.config.js | 22 +- yarn.lock | 8644 +++++++++++++++++ 478 files changed, 20278 insertions(+), 24371 deletions(-) create mode 100644 LICENSE.md create mode 100644 src/components/FacebookButton/index.js create mode 100644 src/components/FacebookTokenButton/index.js create mode 100644 src/components/FacebookTokenButton/style.scss create mode 100644 src/components/Footer/index.js create mode 100644 src/components/Footer/style.scss create mode 100644 src/components/GlobalNav/Surah/index.js create mode 100644 src/components/GlobalNav/index.js create mode 100644 src/components/GlobalNav/style.scss create mode 100644 src/components/GlobalSidebar/Surah/index.js create mode 100644 src/components/GlobalSidebar/index.js create mode 100644 src/components/GlobalSidebar/style.scss delete mode 100644 src/components/Home/LastVisit/style.scss delete mode 100644 src/components/IndexHeader/Nav/index.js create mode 100644 src/components/Loader/style.scss create mode 100644 src/components/LocaleFormattedMessage/index.js create mode 100644 src/components/LocaleSwitcher/index.js create mode 100644 src/components/LocaleSwitcher/style.scss create mode 100644 src/components/NightModeToggle/index.js create mode 100644 src/components/NoScript/index.js create mode 100644 src/components/NoScript/style.scss create mode 100644 src/components/Radio/index.js create mode 100644 src/components/Radio/style.scss create mode 100644 src/components/SettingsModal/index.js create mode 100644 src/components/SmartBanner/index.js delete mode 100644 src/components/SurahInfo/htmls/1.html.js delete mode 100644 src/components/SurahInfo/htmls/10.html.js delete mode 100644 src/components/SurahInfo/htmls/100.html.js delete mode 100644 src/components/SurahInfo/htmls/101.html.js delete mode 100644 src/components/SurahInfo/htmls/102.html.js delete mode 100644 src/components/SurahInfo/htmls/103.html.js delete mode 100644 src/components/SurahInfo/htmls/104.html.js delete mode 100644 src/components/SurahInfo/htmls/105.html.js delete mode 100644 src/components/SurahInfo/htmls/106.html.js delete mode 100644 src/components/SurahInfo/htmls/107.html.js delete mode 100644 src/components/SurahInfo/htmls/108.html.js delete mode 100644 src/components/SurahInfo/htmls/109.html.js delete mode 100644 src/components/SurahInfo/htmls/11.html.js delete mode 100644 src/components/SurahInfo/htmls/110.html.js delete mode 100644 src/components/SurahInfo/htmls/111.html.js delete mode 100644 src/components/SurahInfo/htmls/112.html.js delete mode 100644 src/components/SurahInfo/htmls/113.html.js delete mode 100644 src/components/SurahInfo/htmls/114.html.js delete mode 100644 src/components/SurahInfo/htmls/12.html.js delete mode 100644 src/components/SurahInfo/htmls/13.html.js delete mode 100644 src/components/SurahInfo/htmls/14.html.js delete mode 100644 src/components/SurahInfo/htmls/15.html.js delete mode 100644 src/components/SurahInfo/htmls/16.html.js delete mode 100644 src/components/SurahInfo/htmls/17.html.js delete mode 100644 src/components/SurahInfo/htmls/18.html.js delete mode 100644 src/components/SurahInfo/htmls/19.html.js delete mode 100644 src/components/SurahInfo/htmls/2.html.js delete mode 100644 src/components/SurahInfo/htmls/20.html.js delete mode 100644 src/components/SurahInfo/htmls/21.html.js delete mode 100644 src/components/SurahInfo/htmls/22.html.js delete mode 100644 src/components/SurahInfo/htmls/23.html.js delete mode 100644 src/components/SurahInfo/htmls/24.html.js delete mode 100644 src/components/SurahInfo/htmls/25.html.js delete mode 100644 src/components/SurahInfo/htmls/26.html.js delete mode 100644 src/components/SurahInfo/htmls/27.html.js delete mode 100644 src/components/SurahInfo/htmls/28.html.js delete mode 100644 src/components/SurahInfo/htmls/29.html.js delete mode 100644 src/components/SurahInfo/htmls/3.html.js delete mode 100644 src/components/SurahInfo/htmls/30.html.js delete mode 100644 src/components/SurahInfo/htmls/31.html.js delete mode 100644 src/components/SurahInfo/htmls/32.html.js delete mode 100644 src/components/SurahInfo/htmls/33.html.js delete mode 100644 src/components/SurahInfo/htmls/34.html.js delete mode 100644 src/components/SurahInfo/htmls/35.html.js delete mode 100644 src/components/SurahInfo/htmls/36.html.js delete mode 100644 src/components/SurahInfo/htmls/37.html.js delete mode 100644 src/components/SurahInfo/htmls/38.html.js delete mode 100644 src/components/SurahInfo/htmls/39.html.js delete mode 100644 src/components/SurahInfo/htmls/4.html.js delete mode 100644 src/components/SurahInfo/htmls/40.html.js delete mode 100644 src/components/SurahInfo/htmls/41.html.js delete mode 100644 src/components/SurahInfo/htmls/42.html.js delete mode 100644 src/components/SurahInfo/htmls/43.html.js delete mode 100644 src/components/SurahInfo/htmls/44.html.js delete mode 100644 src/components/SurahInfo/htmls/45.html.js delete mode 100644 src/components/SurahInfo/htmls/46.html.js delete mode 100644 src/components/SurahInfo/htmls/47.html.js delete mode 100644 src/components/SurahInfo/htmls/48.html.js delete mode 100644 src/components/SurahInfo/htmls/49.html.js delete mode 100644 src/components/SurahInfo/htmls/5.html.js delete mode 100644 src/components/SurahInfo/htmls/50.html.js delete mode 100644 src/components/SurahInfo/htmls/51.html.js delete mode 100644 src/components/SurahInfo/htmls/52.html.js delete mode 100644 src/components/SurahInfo/htmls/53.html.js delete mode 100644 src/components/SurahInfo/htmls/54.html.js delete mode 100644 src/components/SurahInfo/htmls/55.html.js delete mode 100644 src/components/SurahInfo/htmls/56.html.js delete mode 100644 src/components/SurahInfo/htmls/57.html.js delete mode 100644 src/components/SurahInfo/htmls/58.html.js delete mode 100644 src/components/SurahInfo/htmls/59.html.js delete mode 100644 src/components/SurahInfo/htmls/6.html.js delete mode 100644 src/components/SurahInfo/htmls/60.html.js delete mode 100644 src/components/SurahInfo/htmls/61.html.js delete mode 100644 src/components/SurahInfo/htmls/62.html.js delete mode 100644 src/components/SurahInfo/htmls/63.html.js delete mode 100644 src/components/SurahInfo/htmls/64.html.js delete mode 100644 src/components/SurahInfo/htmls/65.html.js delete mode 100644 src/components/SurahInfo/htmls/66.html.js delete mode 100644 src/components/SurahInfo/htmls/67.html.js delete mode 100644 src/components/SurahInfo/htmls/68.html.js delete mode 100644 src/components/SurahInfo/htmls/69.html.js delete mode 100644 src/components/SurahInfo/htmls/7.html.js delete mode 100644 src/components/SurahInfo/htmls/70.html.js delete mode 100644 src/components/SurahInfo/htmls/71.html.js delete mode 100644 src/components/SurahInfo/htmls/72.html.js delete mode 100644 src/components/SurahInfo/htmls/73.html.js delete mode 100644 src/components/SurahInfo/htmls/74.html.js delete mode 100644 src/components/SurahInfo/htmls/75.html.js delete mode 100644 src/components/SurahInfo/htmls/76.html.js delete mode 100644 src/components/SurahInfo/htmls/77.html.js delete mode 100644 src/components/SurahInfo/htmls/78.html.js delete mode 100644 src/components/SurahInfo/htmls/79.html.js delete mode 100644 src/components/SurahInfo/htmls/8.html.js delete mode 100644 src/components/SurahInfo/htmls/80.html.js delete mode 100644 src/components/SurahInfo/htmls/81.html.js delete mode 100644 src/components/SurahInfo/htmls/82.html.js delete mode 100644 src/components/SurahInfo/htmls/83.html.js delete mode 100644 src/components/SurahInfo/htmls/84.html.js delete mode 100644 src/components/SurahInfo/htmls/85.html.js delete mode 100644 src/components/SurahInfo/htmls/86.html.js delete mode 100644 src/components/SurahInfo/htmls/87.html.js delete mode 100644 src/components/SurahInfo/htmls/88.html.js delete mode 100644 src/components/SurahInfo/htmls/89.html.js delete mode 100644 src/components/SurahInfo/htmls/9.html.js delete mode 100644 src/components/SurahInfo/htmls/90.html.js delete mode 100644 src/components/SurahInfo/htmls/91.html.js delete mode 100644 src/components/SurahInfo/htmls/92.html.js delete mode 100644 src/components/SurahInfo/htmls/93.html.js delete mode 100644 src/components/SurahInfo/htmls/94.html.js delete mode 100644 src/components/SurahInfo/htmls/95.html.js delete mode 100644 src/components/SurahInfo/htmls/96.html.js delete mode 100644 src/components/SurahInfo/htmls/97.html.js delete mode 100644 src/components/SurahInfo/htmls/98.html.js delete mode 100644 src/components/SurahInfo/htmls/99.html.js delete mode 100644 src/components/SurahInfo/htmls/README.md delete mode 100644 src/components/SurahInfo/htmls/index.js create mode 100644 src/components/Word/index.js create mode 100644 src/containers/App/connect.js create mode 100644 src/containers/Login/index.js create mode 100644 src/containers/Profile/index.js create mode 100644 src/containers/Profile/style.scss create mode 100644 src/containers/Surah/Header/style.scss delete mode 100644 src/containers/Surah/descriptions.js create mode 100644 src/helpers/setLocal.js create mode 100644 src/locale/ar.js create mode 100644 src/locale/en.js create mode 100644 src/locale/ur.js create mode 100644 src/redux/actions/auth.js create mode 100644 src/redux/actions/bookmarks.js create mode 100644 src/redux/actions/media.js create mode 100644 src/redux/actions/suggest.js create mode 100644 src/redux/constants/auth.js create mode 100644 src/redux/constants/bookmarks.js create mode 100644 src/redux/constants/media.js create mode 100644 src/redux/constants/suggest.js create mode 100644 src/redux/modules/auth.js create mode 100644 src/redux/modules/bookmarks.js create mode 100644 src/redux/modules/media.js create mode 100644 src/redux/modules/suggestResults.js create mode 100644 src/styles/components/SmartBanner.scss delete mode 100644 src/styles/components/SurahNav.scss delete mode 100644 src/styles/mixins/_center.scss create mode 100644 src/styles/nightmode.scss delete mode 100644 src/styles/partials/_modal.scss create mode 100644 src/styles/partials/_no-script.scss delete mode 100644 src/styles/partials/_surah-title.scss create mode 100644 src/types/ayahType.js create mode 100644 src/types/bookmarkType.js create mode 100644 src/types/index.js create mode 100644 src/types/matchType.js create mode 100644 src/types/optionsType.js create mode 100644 src/types/segmentType.js create mode 100644 src/types/surahType.js create mode 100644 src/types/userType.js create mode 100644 src/types/wordType.js create mode 100644 src/utils/bindTooltip.js create mode 100644 src/utils/getOffset.js create mode 100644 static/android-chrome-144x144.png create mode 100644 static/android-chrome-192x192.png create mode 100644 static/android-chrome-256x256.png create mode 100644 static/android-chrome-48x48.png create mode 100644 static/apple-touch-icon-114x114-precomposed.png create mode 100644 static/apple-touch-icon-114x114.png create mode 100644 static/apple-touch-icon-120x120-precomposed.png create mode 100644 static/apple-touch-icon-120x120.png create mode 100644 static/apple-touch-icon-144x144-precomposed.png create mode 100644 static/apple-touch-icon-144x144.png create mode 100644 static/apple-touch-icon-152x152-precomposed.png create mode 100644 static/apple-touch-icon-152x152.png create mode 100644 static/apple-touch-icon-57x57-precomposed.png create mode 100644 static/apple-touch-icon-57x57.png create mode 100644 static/apple-touch-icon-60x60-precomposed.png create mode 100644 static/apple-touch-icon-60x60.png create mode 100644 static/apple-touch-icon-72x72-precomposed.png create mode 100644 static/apple-touch-icon-72x72.png create mode 100644 static/apple-touch-icon-76x76-precomposed.png create mode 100644 static/apple-touch-icon-76x76.png create mode 100644 static/apple-touch-icon-precomposed.png create mode 100644 static/apple-touch-icon.png create mode 100644 static/favicon-16x16.png create mode 100644 static/favicon-32x32.png rename static/{images => }/favicon.ico (100%) create mode 100644 static/fonts/nafees/nafees-nastaleeq-webfont.eot create mode 100644 static/fonts/nafees/nafees-nastaleeq-webfont.svg create mode 100644 static/fonts/nafees/nafees-nastaleeq-webfont.ttf create mode 100644 static/fonts/nafees/nafees-nastaleeq-webfont.woff create mode 100644 static/fonts/ss-standard/ss-standard.css create mode 100644 static/images/app-banner-android.png create mode 100755 static/images/app-banner-ios.jpg delete mode 100644 static/images/apple-touch-icon-120x120-precomposed.png delete mode 100644 static/images/apple-touch-icon-120x120-precompressed.png delete mode 100644 static/images/apple-touch-icon-152x152-precomposed.png delete mode 100644 static/images/apple-touch-icon-152x152.png delete mode 100644 static/images/apple-touch-icon-57x57-precomposed.png delete mode 100644 static/images/apple-touch-icon-57x57.png delete mode 100644 static/images/apple-touch-icon-precomposed.png delete mode 100644 static/images/apple-touch-icon.png create mode 100644 static/manifest.json create mode 100755 static/mstitle-150x150.jpg create mode 100755 static/mstitle-310x150.jpg create mode 100755 static/mstitle-310x310.jpg create mode 100755 static/mstitle-70x70.jpg create mode 100644 static/opensearch.xml create mode 100644 static/quran-service-worker.js create mode 100644 static/safari-pinned-tab.svg create mode 100644 tests/helpers/intl-enzyme-test-helper.js create mode 100644 yarn.lock diff --git a/.env b/.env index 610c409b2..2c85a0ee9 100644 --- a/.env +++ b/.env @@ -1,6 +1,9 @@ NODE_ENV=development PORT=8000 -API_URL=http://quran.com:3000 +API_URL=http://staging.quran.com:3000 +ONE_QURAN_URL=http://localhost:3030 SEGMENTS_KEY= SENTRY_KEY_CLIENT= SENTRY_KEY_SERVER= +# Quran.com - development app, no need to worry! +FACEBOOK_APP_ID=1599019233731707 diff --git a/.eslintignore b/.eslintignore index 1ee2668e7..e69de29bb 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +0,0 @@ -src/components/SurahInfo/htmls/* diff --git a/.eslintrc b/.eslintrc index a277bf4a8..58b1abbaf 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,39 +1,24 @@ { - "parser": "babel-eslint", "extends": "airbnb", - "env": { - "browser": true, - "node": true, - "mocha": true, - "es6": true - }, + "parser": "babel-eslint", "rules": { - "react/no-multi-comp": 0, - "import/default": 0, - "import/no-duplicates": 0, - "import/named": 0, - "import/namespace": 0, + "comma-dangle": 0, + "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], + "import/no-extraneous-dependencies": 0, + "import/extensions": 0, "import/no-unresolved": 0, - "import/no-named-as-default": 2, - // Temporarirly disabled due to a possible bug in babel-eslint (todomvc example) - "block-scoped-var": 0, - // Temporarily disabled for test/* until babel/babel-eslint#33 is resolved - "padded-blocks": 0, - "comma-dangle": 0, // not sure why airbnb turned this on. gross! - "indent": [2, 2, {"SwitchCase": 1}], - "no-console": 0, - "no-alert": 0, - "object-curly-spacing": 0, - "no-case-declarations": 0 + "strict": 0 + }, + "ecmaFeatures": { + "classes": true, + "jsx": true }, "plugins": [ - "react", "import" + "react", + "mocha" ], "settings": { - "import/parser": "babel-eslint", - "import/resolve": { - moduleDirectory: ["node_modules", "src"] - } + "import/resolver": "webpack" }, "parserOptions":{ "ecmaFeatures": { @@ -52,6 +37,12 @@ Raven: true, mixpanel: true, "expect": true, - "browser": true + "browser": true, + "FB": true, + sinon: true + }, + "env": { + "mocha": true, + "amd": true } } diff --git a/.gitignore b/.gitignore index 135ab60a2..707bb3209 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ tests/functional/output/* test/functional/screenshots/* .ssh webpack-stats.debug.json +*.DS_Store diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 219a7a38c..e35fbfe63 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -75,15 +75,8 @@ Pull requests are the greatest contributions, so be sure they are focused in sco 6. Now [open a pull request] with a clear title and description. -## Sever-side integration -Unless you have the backend API running locally, you will need to update the `API_URL`, in `development.env` file, from `localhost` to `api.quran.com`. Leave the port number same. - -To start the app, run `npm run dev` which will run both the server and the client (webpack) to compile upon edits. Go to http://localhost:8001 in your browser, not 8000 (that is just the express server). - -If you experience an issue, check the [contributing] guidelines. [upstream]: https://help.github.com/articles/syncing-a-fork/ -[contributing]: https://guides.github.com/activities/contributing-to-open-source/ [already been reported]: https://github.com/quran/quran.com-frontend/issues [fork this project]: https://github.com/quran/quran.com-frontend/fork [open a pull request]: https://help.github.com/articles/using-pull-requests/ diff --git a/Dockerfile b/Dockerfile index e3f6568dc..a43d27e73 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,9 @@ ENV NODE_ENV production ENV API_URL http://api.quran.com:3000 ENV SENTRY_KEY_CLIENT https://44c105328ae544ae9928f9eb74b40061@app.getsentry.com/80639 ENV SENTRY_KEY_SERVER https://44c105328ae544ae9928f9eb74b40061:41ca814d33124e04ab450104c3938cb1@app.getsentry.com/80639 +# It's okay because it's only the APP ID +ENV FACEBOOK_APP_ID 1557596491207315 +ENV ONE_QURAN_URL https://one.quran.com ENV PORT 8000 ENV NODE_PATH "./src" @@ -26,6 +29,7 @@ RUN cp -a /tmp/node_modules /quran WORKDIR /quran ADD . /quran/ + RUN npm run build:client RUN npm run build:server diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 000000000..d932105ee --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Quran.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 71c29abb7..3a973e5f6 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,35 @@ - # Quran.com -This is the project soon to be the Quran.com facing site. This is built in +This project is the frontend for Quran.com. It is built using [Reactjs] + [Redux] + [Expressjs] + [Webpack]. It is isomorphic (javascript shared between both the server and the client) for SEO reasons. -[![Stories Ready](https://badge.waffle.io/quran/quran.com-frontend.svg?label=ready&title=Ready)](http://waffle.io/quran/quran.com-frontend) -[![Stories In Progress](https://badge.waffle.io/quran/quran.com-frontend.svg?label=in%20progress&title=In%20Progress)](http://waffle.io/quran/quran.com-frontend) -[![Stories In Review](https://badge.waffle.io/quran/quran.com-frontend.svg?label=in%20review&title=In%20Review)](http://waffle.io/quran/quran.com-frontend) - - -[![Dependency Status](https://david-dm.org/quran/quran.com-frontend.svg)](https://david-dm.org/quran/quran.com-frontend) [![devDependency Status](https://david-dm.org/quran/quran.com-frontend/dev-status.svg)](https://david-dm.org/quran/quran.com-frontend#info=devDependencies) [![Code Climate](https://codeclimate.com/github/quran/quran.com-frontend.png)](https://codeclimate.com/github/quran/quran.com-frontend) ## How to contribute -We trust that you will not copy this idea/project, this is at the end for the sake of Allah and we all have good intentions while working with this project. But We must stress that copying the code/project is unacceptable. +We trust that you will not copy this idea/project, this is at the end for the sake of Allah and we all have good intentions while working with this project. But we must stress that copying the code/project is unacceptable. Read the [contributing] section before creating an issue. -## Server-Side Integration -Unless you have the backend API running locally, you will need to update the `API_URL`, in `development.env` file, from `localhost` to `api.quran.com`. Leave the port number same. - -To start the app, run `npm run dev` which will run both the server and the client (webpack) to compile upon edits. Go to http://localhost:8001 in your browser, not 8000 (that is just the express server). +## Running the app locally +- Ensure you have [nodejs] installed +- Get the source by running `git clone https://github.com/quran/quran.com-frontend/` or creating a [fork] +- Run `npm install` to do first time installation of all dependencies +- Run `npm run dev` to start the dev server +- Open `http://localhost:8000` in your browser to see the app. +## Staging +To see the app with the latest changes, see the [staging] site. Production releases are made periodically when staging is stable and well tested. ## Backend -Current at: https://github.com/quran/quran-api-rails -DB is private, message me for access. +The API source is at https://github.com/quran/quran-api-rails + +DB is private, message @mmahalwy for access. + +The dev server uses the staging API by default. If you want to use a local API server, follow the instructions in the API repo and run the server locally then update the API_URL field in app.json to point to the local address. +## Slack +Signup at https://quranslack.herokuapp.com to be added to the Slack group ## Design We currently use InvisionApp. Again, contact me if you'd like access to it. @@ -44,4 +46,7 @@ analyze-bundle-size bundle-stats.json [Redux]: http://redux.js.org/ [Expressjs]: http://expressjs.com/en/starter/hello-world.html [Webpack]: http://webpack.github.io/docs/what-is-webpack.html +[nodejs]: https://nodejs.org/en/ [contributing]: CONTRIBUTING.md +[fork]: https://help.github.com/articles/fork-a-repo/ +[staging]: https://staging.quran.com diff --git a/karma.conf.js b/karma.conf.js index d21d97f4a..ac9b1aa40 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -13,17 +13,19 @@ module.exports = function(config) { 'karma-sinon', 'karma-webpack', 'karma-chrome-launcher', - 'karma-phantomjs-launcher' + 'karma-phantomjs-launcher', + 'karma-intl-shim' ], // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['mocha', 'chai-sinon', 'sinon'], + frameworks: ['mocha', 'chai-sinon', 'sinon', 'intl-shim'], // list of files / patterns to load in the browser files: [ './node_modules/phantomjs-polyfill/bind-polyfill.js', './tests/polyfill/Event.js', + './node_modules/Intl/locale-data/jsonp/en-US.js', {pattern: 'static/images/*', watched: false, included: false, served: true}, // Actual tests here diff --git a/package.json b/package.json index 242a0cc72..0d4313466 100644 --- a/package.json +++ b/package.json @@ -5,14 +5,15 @@ "repository": "https://github.com/quran/quran.com-frontend", "scripts": { "test": "npm run test:dev:unit", - "test:ci:unit": "karma start --browsers PhantomJS --single-run; npm run test:ci:lint", + "test:ci:unit": "karma start --browsers PhantomJS --single-run", + "pretest:ci:unit": "npm run test:ci:lint", "test:ci:functional": "BROWSER=phantomjs ./tests/functional/test.sh start-ci", "posttest:ci:functional": "./tests/functional/test.sh stop", "test:dev:unit": "karma start", - "test:ci:lint": "eslint ./src/**/*.js", + "test:ci:lint": "./node_modules/eslint/bin/eslint.js ./src", "test:dev:functional": "BROWSER=chrome ./tests/functional/test.sh start", "posttest:dev:functional": "./tests/functional/test.sh stop", - "test:dev:lint": "eslint ./src/scripts/**/*.js", + "test:dev:lint": "./node_modules/eslint/bin/eslint.js ./src", "test:stylelint": "stylelint './src/**/*.scss' --config ./webpack/.stylelintrc", "dev": "env NODE_PATH='./src' PORT=8000 UV_THREADPOOL_SIZE=100 node ./webpack/webpack-dev-server.js & env NODE_PATH='./src' PORT=8000 node ./bin/server.js", "start": "NODE_PATH='src' node ./start", @@ -38,6 +39,7 @@ "babel-plugin-transform-react-constant-elements": "6.9.1", "babel-plugin-transform-react-display-name": "6.8.0", "babel-plugin-transform-react-inline-elements": "6.8.0", + "babel-plugin-transform-react-remove-prop-types": "0.2.11", "babel-plugin-transform-runtime": "6.12.0", "babel-plugin-typecheck": "3.9.0", "babel-polyfill": "6.13.0", @@ -54,59 +56,65 @@ "cache-manager": "1.5.0", "clean-webpack-plugin": "0.1.10", "compression": "1.6.2", + "compression-webpack-plugin": "0.3.2", "cookie-parser": "1.4.3", - "copy-to-clipboard": "1.1.1", + "copy-to-clipboard": "3.0.5", "cors": "2.7.1", - "crypto-js": "3.1.6", "css-loader": "0.23.1", "debug": "2.2.0", - "dotenv": "1.2.0", + "dotenv": "2.0.0", "errorhandler": "1.4.3", "express": "4.14.0", "express-state": "1.4.0", - "express-useragent": "0.2.4", + "express-useragent": "1.0.4", "extract-text-webpack-plugin": "2.0.0-beta.3", "file-loader": "0.8.5", "fontfaceobserver": "1.7.3", + "history": "^3.0.0", "html-webpack-plugin": "1.7.0", "http-proxy": "1.14.0", - "humps": "1.1.0", + "humps": "2.0.0", "imports-loader": "0.6.5", - "jquery": "2.2.4", "json-loader": "0.5.4", "morgan": "1.7.0", - "node-sass": "3.8.0", - "normalizr": "2.2.1", + "node-sass": "4.1.1", + "normalizr": "3.0.2", "pretty-error": "2.0.0", "promise": "7.1.1", "proxy-middleware": "0.14.0", "qs": "6.2.1", - "raven": "0.11.0", + "raven": "1.1.1", "raw-loader": "0.5.1", - "react": "0.14.8", - "react-bootstrap": "0.29.5", - "react-cookie": "0.3.4", - "react-dom": "0.14.8", + "react": "15.4.1", + "react-a11y": "0.3.3", + "react-addons-create-fragment": "15.4.1", + "react-bootstrap": "0.30.7", + "react-cookie": "1.0.4", + "react-dom": "15.4.1", "react-helmet": "3.1.0", + "react-inlinesvg": "0.5.4", + "react-intl": "2.1.5", "react-metrics": "1.2.1", - "react-paginate": "0.4.3", - "react-redux": "4.4.5", - "react-router": "2.6.1", + "react-paginate": "4.1.0", + "react-redux": "5.0.1", + "react-router": "3.0.0", "react-router-bootstrap": "0.20.1", - "react-router-redux": "4.0.5", + "react-router-redux": "4.0.7", "react-router-scroll": "0.2.1", "react-scroll": "1.2.0", + "react-share": "1.11.0", + "react-sidebar": "2.2.1", "redux": "3.5.2", - "redux-connect": "2.4.0", + "redux-connect": "5.0.0", "reselect": "2.5.3", "resolve-url": "0.2.1", - "sass-loader": "2.0.1", + "sass-loader": "4.1.1", "serialize-javascript": "1.3.0", "serve-favicon": "2.3.0", "sitemap": "1.8.1", "strip-loader": "0.1.2", "style-loader": "0.13.1", - "superagent": "1.8.4", + "superagent": "3.3.1", "url": "0.11.0", "url-loader": "0.5.7", "webpack": "2.1.0-beta.20", @@ -114,27 +122,29 @@ "winston": "1.1.2" }, "devDependencies": { - "babel-eslint": "6.0.4", + "babel-eslint": "7.1.1", "babel-plugin-react-transform": "2.0.2", "babel-preset-react-hmre": "1.1.1", "chai": "3.0.0", "chromedriver": "2.22.2", "del": "2.0.2", "enzyme": "2.2.0", - "eslint": "2.13.0", - "eslint-config-airbnb": "9.0.1", + "eslint": "3.12.2", + "eslint-config-airbnb": "13.0.0", "eslint-loader": "1.3.0", - "eslint-plugin-import": "1.8.1", - "eslint-plugin-jsx-a11y": "1.5.3", - "eslint-plugin-react": "5.2.2", + "eslint-plugin-import": "2.2.0", + "eslint-plugin-jsx-a11y": "2.2.3", + "eslint-plugin-mocha": "4.8.0", + "eslint-plugin-react": "6.8.0", "jscs": "2.1.1", - "karma": "1.2.0", + "karma": "1.3.0", "karma-chai": "0.1.0", "karma-chai-sinon": "0.1.5", "karma-chrome-launcher": "0.2.0", + "karma-intl-shim": "1.0.3", "karma-junit-reporter": "0.3.4", "karma-mocha": "0.2.0", - "karma-phantomjs-launcher": "~0.2.1", + "karma-phantomjs-launcher": "~1.0.2", "karma-script-launcher": "~0.1.0", "karma-sinon": "1.0.4", "karma-sourcemap-loader": "0.3.7", @@ -146,7 +156,7 @@ "phantomjs-polyfill": "0.0.1", "piping": "0.3.0", "pre-commit": "1.1.3", - "react-addons-test-utils": "0.14.7", + "react-addons-test-utils": "15.4.1", "react-transform-catch-errors": "1.0.0", "react-transform-hmr": "1.0.1", "redbox-react": "1.1.1", @@ -161,6 +171,7 @@ "wdio-mocha-framework": "0.3.7", "wdio-spec-reporter": "0.0.3", "webdriverio": "4.2.1", + "webpack-bundle-analyzer": "2.2.1", "webpack-dev-server": "1.6.5", "webpack-hot-middleware": "2.12.2" }, diff --git a/src/client.js b/src/client.js index 6ee43334d..cb2f06a95 100644 --- a/src/client.js +++ b/src/client.js @@ -12,6 +12,7 @@ import applyRouterMiddleware from 'react-router/lib/applyRouterMiddleware'; import useScroll from 'react-router-scroll'; import { ReduxAsyncConnect } from 'redux-connect'; import { syncHistoryWithStore } from 'react-router-redux'; +import { IntlProvider } from 'react-intl'; import debug from 'debug'; @@ -19,6 +20,7 @@ import config from './config'; import ApiClient from './helpers/ApiClient'; import createStore from './redux/create'; import routes from './routes'; +import getLocalMessages from './helpers/setLocal'; const client = new ApiClient(); const store = createStore(browserHistory, client, window.reduxData); @@ -27,7 +29,7 @@ const history = syncHistoryWithStore(browserHistory, store); try { Raven.config(config.sentryClient).install(); } catch (error) { - console.log(error); + debug('client', error); } window.quranDebug = debug; @@ -38,17 +40,19 @@ window.clearCookies = () => { reactCookie.remove('content'); reactCookie.remove('audio'); reactCookie.remove('isFirstTime'); + reactCookie.remove('currentLocale'); + reactCookie.remove('smartbanner-closed'); + reactCookie.remove('smartbanner-installed'); }; -match({ history, routes: routes() }, (error, redirectLocation, renderProps) => { +match({ history, routes: routes(store) }, (error, redirectLocation, renderProps) => { const component = ( ( + render={props => ( !item.deferred} + helpers={{ client }} render={applyRouterMiddleware(useScroll())} /> )} @@ -60,9 +64,11 @@ match({ history, routes: routes() }, (error, redirectLocation, renderProps) => { debug('client', 'React Rendering'); ReactDOM.render( - - {component} - , mountNode, () => { + + + {component} + + , mountNode, () => { debug('client', 'React Rendered'); } ); diff --git a/src/components/Audioplayer/RepeatDropdown/index.js b/src/components/Audioplayer/RepeatDropdown/index.js index 345d32ddf..d4f0379d7 100644 --- a/src/components/Audioplayer/RepeatDropdown/index.js +++ b/src/components/Audioplayer/RepeatDropdown/index.js @@ -4,23 +4,27 @@ import Popover from 'react-bootstrap/lib/Popover'; import Nav from 'react-bootstrap/lib/Nav'; import NavItem from 'react-bootstrap/lib/NavItem'; import FormControl from 'react-bootstrap/lib/FormControl'; -import Row from 'react-bootstrap/lib/Row'; import Col from 'react-bootstrap/lib/Col'; +import { intlShape, injectIntl } from 'react-intl'; import SwitchToggle from 'components/SwitchToggle'; +import LocaleFormattedMessage from 'components/LocaleFormattedMessage'; + +import surahType from 'types/surahType'; const style = require('../style.scss'); -export default class RepeatButton extends Component { +class RepeatButton extends Component { static propTypes = { - surah: PropTypes.object.isRequired, + surah: surahType, repeat: PropTypes.shape({ from: PropTypes.number, to: PropTypes.number, times: PropTypes.number }).isRequired, setRepeat: PropTypes.func.isRequired, - current: PropTypes.number.isRequired + current: PropTypes.number.isRequired, + intl: intlShape.isRequired }; handleToggle = () => { @@ -58,14 +62,18 @@ export default class RepeatButton extends Component { const array = Array(surah.ayat).join().split(','); return ( - - From - To:
-
    + +
    • + {' '}: +
      setRepeat({ + onChange={event => setRepeat({ ...repeat, from: parseInt(event.target.value, 10), to: parseInt(event.target.value, 10) + 3 @@ -82,10 +90,15 @@ export default class RepeatButton extends Component {
    • -
    • + {' '}: +
      setRepeat({ ...repeat, to: parseInt(event.target.value, 10)})} + onChange={event => setRepeat({ ...repeat, to: parseInt(event.target.value, 10) })} > { array.map((ayah, index) => ( @@ -106,12 +119,15 @@ export default class RepeatButton extends Component { const array = Array(surah.ayat).join().split(','); return ( - - Ayah:
      + + {' '}:
      setRepeat({ + onChange={event => setRepeat({ ...repeat, from: parseInt(event.target.value, 10), to: parseInt(event.target.value, 10) @@ -133,7 +149,7 @@ export default class RepeatButton extends Component { const { repeat } = this.props; return ( - +
      - +
      ); } @@ -156,30 +178,35 @@ export default class RepeatButton extends Component { const { repeat } = this.props; return ( - +
      {repeat.from === repeat.to ? this.renderSingleAyah() : this.renderRangeAyahs()} - +
      ); } renderTimes() { - const { repeat, setRepeat } = this.props; + const { repeat, setRepeat, intl } = this.props; const times = Array(10).join().split(','); return ( - - - Times:
      +
      + + :
      setRepeat({ + onChange={event => setRepeat({ ...repeat, times: parseInt(event.target.value, 10) })} > - { times.map((ayah, index) => ( @@ -190,7 +217,7 @@ export default class RepeatButton extends Component { } - +
      ); } @@ -202,9 +229,12 @@ export default class RepeatButton extends Component { id="FontSizeDropdown" className={style.popover} title={ - +
      - Toggle repeat{' '} + {' '} - +
      } > {this.renderNav()} @@ -225,7 +255,7 @@ export default class RepeatButton extends Component {
      @@ -237,3 +267,5 @@ export default class RepeatButton extends Component { ); } } + +export default injectIntl(RepeatButton); diff --git a/src/components/Audioplayer/RepeatDropdown/spec.js b/src/components/Audioplayer/RepeatDropdown/spec.js index 59ff9107a..7d89fb2ed 100644 --- a/src/components/Audioplayer/RepeatDropdown/spec.js +++ b/src/components/Audioplayer/RepeatDropdown/spec.js @@ -1,17 +1,19 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { mountWithIntl } from '../../../../tests/helpers/intl-enzyme-test-helper.js'; import RepeatDropdown from './index'; -let makeComponent, component, overlay, setRepeat; +let component; +let overlay; +let setRepeat; const surah = { ayat: 10 }; -makeComponent = (repeat) => { +const makeComponent = (repeat) => { setRepeat = sinon.stub(); - component = mount( + component = mountWithIntl( { /> ); - overlay = mount(component.find('OverlayTrigger').first().props().overlay); -} + overlay = mountWithIntl(component.find('OverlayTrigger').first().props().overlay); +}; describe('', () => { it('should not be repeating', () => { - makeComponent({times: Infinity}); + makeComponent({ times: Infinity }); expect(component.find('i').first().props().className).not.to.contain('repeat_'); }); it('should indicate repeating', () => { - makeComponent({from: 1, to: 10, times: Infinity}); + makeComponent({ from: 1, to: 10, times: Infinity }); expect(component.find('i').first().props().className).to.contain('repeat'); }); describe('when single ayah', () => { beforeEach(() => { - makeComponent({from: 3, to: 3, times: Infinity}); + makeComponent({ from: 3, to: 3, times: Infinity }); }); it('should have a single ayah input', () => { @@ -52,7 +54,7 @@ describe('', () => { describe('when range', () => { beforeEach(() => { - makeComponent({from: 1, to: 3, times: Infinity}); + makeComponent({ from: 1, to: 3, times: Infinity }); }); it('should have a range ayah input', () => { @@ -67,13 +69,13 @@ describe('', () => { describe('times', () => { it('should have Infinity count', () => { - makeComponent({from: 1, to: 3, times: Infinity}); + makeComponent({ from: 1, to: 3, times: Infinity }); expect(overlay.find('FormControl').last().props().value).to.eql(Infinity); }); it('should have a count', () => { - makeComponent({from: 1, to: 3, times: 4}); + makeComponent({ from: 1, to: 3, times: 4 }); expect(overlay.find('FormControl').last().props().value).to.eql(4); }); diff --git a/src/components/Audioplayer/ScrollButton/index.js b/src/components/Audioplayer/ScrollButton/index.js index 1b4ccf98a..cbbce363d 100644 --- a/src/components/Audioplayer/ScrollButton/index.js +++ b/src/components/Audioplayer/ScrollButton/index.js @@ -1,31 +1,35 @@ import React, { PropTypes } from 'react'; import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; import Tooltip from 'react-bootstrap/lib/Tooltip'; +import LocaleFormattedMessage from 'components/LocaleFormattedMessage'; const style = require('../style.scss'); const ScrollButton = ({ shouldScroll, onScrollToggle }) => { const tooltip = ( - Automatically scrolls to the currently playing ayah on transitions... + ); return (
      - - +
      ); diff --git a/src/components/Audioplayer/ScrollButton/spec.js b/src/components/Audioplayer/ScrollButton/spec.js index fac524805..11974d241 100644 --- a/src/components/Audioplayer/ScrollButton/spec.js +++ b/src/components/Audioplayer/ScrollButton/spec.js @@ -3,7 +3,9 @@ import { mount } from 'enzyme'; import ScrollButton from './index'; -let makeComponent, component, onScrollToggle; +let makeComponent; +let component; +let onScrollToggle; describe('', () => { beforeEach(() => { @@ -13,24 +15,24 @@ describe('', () => { component = mount( ); - } + }; }); it('should indicate that shouldScroll', () => { makeComponent(true); - expect(component.find('label').first().props().className).to.contain('scroll'); + expect(component.find('a').first().props().className).to.contain('scroll'); }); it('should not indicate that shouldScroll', () => { makeComponent(false); - expect(component.find('label').first().props().className).not.to.contain('scroll'); + expect(component.find('a').first().props().className).not.to.contain('scroll'); }); it('should call onScrollToggle when clicked', () => { - component.find('label').first().simulate('click'); + component.find('a').first().simulate('click'); - expect(onScrollToggle).to.have.been.called; + expect(onScrollToggle).to.have.been.called; // eslint-disable-line }); }); diff --git a/src/components/Audioplayer/Segments/index.js b/src/components/Audioplayer/Segments/index.js index 38ca53d6f..f32ed272b 100644 --- a/src/components/Audioplayer/Segments/index.js +++ b/src/components/Audioplayer/Segments/index.js @@ -1,19 +1,18 @@ import React, { Component, PropTypes } from 'react'; import Helmet from 'react-helmet'; +import { segmentType } from 'types'; import debug from 'helpers/debug'; export default class Segments extends Component { static propTypes = { - audio: PropTypes.object, - segments: PropTypes.object.isRequired, + segments: PropTypes.objectOf(segmentType).isRequired, currentAyah: PropTypes.string, currentTime: PropTypes.number }; shouldComponentUpdate(nextProps) { return [ - this.props.audio !== nextProps.audio, this.props.currentAyah !== nextProps.currentAyah, this.props.currentTime !== nextProps.currentTime, ].some(test => test); @@ -26,7 +25,7 @@ export default class Segments extends Component { if (!Object.keys(segments).length) return