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

Commit

Permalink
Add user bookmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
mmahalwy committed Nov 13, 2016
1 parent 12e87d6 commit b4b5bf5
Show file tree
Hide file tree
Showing 15 changed files with 312 additions and 48 deletions.
48 changes: 39 additions & 9 deletions src/components/Ayah/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ export default class Ayah extends Component {
static propTypes = {
isSearched: PropTypes.bool,
ayah: PropTypes.object.isRequired,
bookmarked: PropTypes.bool.isRequired,
bookmarkActions: PropTypes.object,
match: PropTypes.array,
isSearch: PropTypes.bool,
isPlaying: PropTypes.bool,
isAuthenticated: PropTypes.bool,
tooltip: PropTypes.string,
currentWord: PropTypes.any, // gets passed in an integer, null by default
onWordClick: PropTypes.func,
actions: PropTypes.object
audioActions: PropTypes.object.isRequired
};

static defaultProps = {
Expand All @@ -37,6 +39,7 @@ export default class Ayah extends Component {
shouldComponentUpdate(nextProps) {
const conditions = [
this.props.ayah !== nextProps.ayah,
this.props.bookmarked !== nextProps.bookmarked,
this.props.tooltip !== nextProps.tooltip,
this.props.currentWord !== nextProps.currentWord
];
Expand All @@ -49,8 +52,8 @@ export default class Ayah extends Component {
}

handlePlay(ayah) {
const {isPlaying, actions} = this.props;
const {pause, setAyah, play} = actions;
const { isPlaying, audioActions } = this.props;
const { pause, setAyah, play } = audioActions;

if (isPlaying) {
pause();
Expand Down Expand Up @@ -85,15 +88,15 @@ export default class Ayah extends Component {
}

renderText() {
const { ayah, onWordClick, tooltip } = this.props;
const { ayah, audioActions: { setCurrentWord }, tooltip } = this.props;

if (!ayah.words[0].code) {
return false;
}

// position is important as it will differentiate between words and symbols, see 2:25:13
let position = -1;
let text = ayah.words.map((word, index) => {
const text = ayah.words.map((word, index) => {
let id = null;
const isLast = ayah.words.length === index + 1;
const className = `${word.className} ${word.highlight ? word.highlight : ''}`;
Expand All @@ -106,13 +109,13 @@ export default class Ayah extends Component {
}

if (word.translation || word.transliteration) {
let tooltipContent = word[tooltip];
const tooltipContent = word[tooltip];

return (
<b
key={word.code}
id={id}
onClick={(event) => onWordClick(event.target.dataset.key)}
onClick={(event) => setCurrentWord(event.target.dataset.key)}
data-key={`${word.ayahKey}:${position}`}
className={`${className} ${styles.Tooltip}`}
aria-label={tooltipContent}
Expand All @@ -125,7 +128,7 @@ export default class Ayah extends Component {
return (
<b
id={id}
onClick={(event) => onWordClick(event.target.dataset.key)}
onClick={(event) => setCurrentWord(event.target.dataset.key)}
data-key={`${word.ayahKey}:${position}`}
className={`${className} ${isLast && styles.Tooltip} pointer`}
key={word.code}
Expand Down Expand Up @@ -177,6 +180,32 @@ export default class Ayah extends Component {
return false;
}

renderBookmark() {
const { ayah, bookmarked, isAuthenticated, bookmarkActions } = this.props;

if (!isAuthenticated) return false;

if (bookmarked) {
return (
<a
onClick={() => bookmarkActions.removeBookmark(ayah.ayahKey)}
className="text-muted"
>
<strong><i className="ss-icon ss-bookmark" /> Bookmarked</strong>
</a>
);
}

return (
<a
onClick={() => bookmarkActions.addBookmark(ayah.ayahKey)}
className="text-muted"
>
<i className="ss-icon ss-bookmark" /> Bookmark
</a>
);
}

renderAyahBadge() {
const { isSearched } = this.props;
const content = (
Expand Down Expand Up @@ -213,6 +242,7 @@ export default class Ayah extends Component {
{this.renderAyahBadge()}
{this.renderPlayLink()}
{this.renderCopyLink()}
{this.renderBookmark()}
</div>
);
}
Expand Down
12 changes: 12 additions & 0 deletions src/containers/App/connect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { isLoaded as isAuthLoaded } from 'redux/actions/auth';
import { load as loadBookmarks } from 'redux/actions/bookmarks';

export const authConnect = ({ store: { getState, dispatch } }) => {
const promises = [];

if (isAuthLoaded(getState())) {
promises.push(dispatch(loadBookmarks()));
}

return Promise.all(promises);
};
6 changes: 5 additions & 1 deletion src/containers/App/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import React, { Component, PropTypes } from 'react';
import { metrics } from 'react-metrics';
import { connect } from 'react-redux';
import { asyncConnect } from 'redux-connect';
import Link from 'react-router/lib/Link';
import Helmet from 'react-helmet';

Expand All @@ -12,6 +13,7 @@ import Col from 'react-bootstrap/lib/Col';
import debug from '../../helpers/debug';
import config from '../../config';
import metricsConfig from '../../helpers/metrics';
import { authConnect } from './connect';

import FontStyles from 'components/FontStyles';

Expand Down Expand Up @@ -108,4 +110,6 @@ class App extends Component {

const metricsApp = metrics(metricsConfig)(App);

export default connect(state => ({surahs: state.surahs.entities }))(metricsApp);
const AsyncApp = asyncConnect([{ promise: authConnect }])(metricsApp);

export default connect(state => ({surahs: state.surahs.entities }))(AsyncApp);
73 changes: 52 additions & 21 deletions src/containers/Profile/index.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,72 @@
import React, { Component, PropTypes } from 'react';
import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import { Link } from 'react-router';

import Grid from 'react-bootstrap/lib/Grid';
import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';
import Image from 'react-bootstrap/lib/Image';
import Tabs from 'react-bootstrap/lib/Tabs';
import Tab from 'react-bootstrap/lib/Tab';

import QuranNav from 'components/QuranNav';
import userType from 'types/userType';

const styles = require('./style.scss');

export const Profile = ({ user }) => (
<div>
<Helmet title="The Noble Quran - القرآن الكريم" titleTemplate="%s" />
<QuranNav />
<div className={styles.header} />
<Grid>
<Row>
<Col md={12} className="text-center">
<Image src={`${user.image}?type=large`} circle className={styles.image} />
<h2>
{user.name}
</h2>
</Col>
</Row>
</Grid>
</div>
);
class Profile extends Component {
static propTypes = {
user: PropTypes.shape(userType),
bookmarks: PropTypes.object.isRequired
};

render() {
const { user, bookmarks } = this.props;

Profile.propTypes = {
user: PropTypes.shape(userType)
};
return (
<div>
<Helmet title="The Noble Quran - القرآن الكريم" titleTemplate="%s" />
<QuranNav />
<div className={styles.header} />
<Grid>
<Row>
<Col md={12} className="text-center">
<Image src={`${user.image}?type=large`} circle className={styles.image} />
<h2>
{user.name}
</h2>
</Col>
</Row>
<Row>
<Col md={6} mdOffset={3}>
<Tabs bsStyle="pills" defaultActiveKey={1} className={styles.tabs} id="tabs">
<Tab eventKey={1} title="Bookmarks">
<ul className="list-group">
{
Object.values(bookmarks).map(bookmark => (
<Link to={bookmark.ayahKey.split(':').join('/')} className="list-group-item">
{bookmark.ayahKey}
</Link>
))
}
</ul>
</Tab>
<Tab eventKey={2} title="Notes">
Notes...
</Tab>
</Tabs>
</Col>
</Row>
</Grid>
</div>
);
}
}

export default connect(
state => ({
user: state.auth.user
user: state.auth.user,
bookmarks: state.bookmarks.entities
})
)(Profile);
39 changes: 39 additions & 0 deletions src/containers/Profile/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,42 @@
display: block;
height: 10rem;
}

.tabs{
:global(.nav-pills){
border-bottom: 1px solid $brand-primary;

:global(li){
:global(a){
background: #fff;
color: $text-color;
font-size: 1.2rem;

&:hover{
background: #f7f7f7;
color: $brand-primary;
}

&:active, &:focus, &:visited{
color: $brand-primary;
background: #fff;
outline: none;
}
}
}

:global(li.active){
border-bottom: 2px solid $brand-primary;


:global(a){
color: $brand-primary;

&:hover{
background: transparent;
color: $brand-primary;
}
}
}
}
}
24 changes: 20 additions & 4 deletions src/containers/Surah/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { surahsConnect, ayahsConnect } from './connect';

import * as AudioActions from '../../redux/actions/audioplayer.js';
import * as AyahActions from '../../redux/actions/ayahs.js';
import * as BookmarkActions from '../../redux/actions/bookmarks.js';
import * as OptionsActions from '../../redux/actions/options.js';

const style = require('./style.scss');
Expand All @@ -55,8 +56,10 @@ class Surah extends Component {
ayahIds: PropTypes.any,
currentWord: PropTypes.string,
surahs: PropTypes.object.isRequired,
bookmarks: PropTypes.object.isRequired,
isLoading: PropTypes.bool.isRequired,
isLoaded: PropTypes.bool.isRequired,
isAuthenticated: PropTypes.bool.isRequired,
options: PropTypes.object.isRequired,
params: PropTypes.object.isRequired,
ayahs: PropTypes.object.isRequired,
Expand Down Expand Up @@ -99,6 +102,7 @@ class Surah extends Component {
this.props.isEndOfSurah !== nextProps.isEndOfSurah,
this.props.ayahIds.length !== nextProps.ayahIds.length,
this.props.surahs !== nextProps.surahs,
this.props.bookmarks !== nextProps.bookmarks,
this.props.isLoading !== nextProps.isLoading,
this.props.isLoaded !== nextProps.isLoaded,
this.props.options !== nextProps.options
Expand Down Expand Up @@ -286,15 +290,24 @@ class Surah extends Component {
}

renderAyahs() {
const { ayahs, actions, options, isPlaying } = this.props; // eslint-disable-line no-shadow
const {
ayahs,
actions,
options,
bookmarks,
isPlaying,
isAuthenticated
} = this.props; // eslint-disable-line no-shadow

return Object.values(ayahs).map(ayah => (
<Ayah
ayah={ayah}
bookmarked={!!bookmarks[ayah.ayahKey]}
tooltip={options.tooltip}
onWordClick={actions.audio.setCurrentWord}
actions={actions.audio}
bookmarkActions={actions.bookmark}
audioActions={actions.audio}
isPlaying={isPlaying}
isAuthenticated={isAuthenticated}
key={`${ayah.surahId}-${ayah.ayahNum}-ayah`}
/>
));
Expand Down Expand Up @@ -435,9 +448,11 @@ function mapStateToProps(state, ownProps) {
ayahIds,
isStarted: state.audioplayer.isStarted,
isPlaying: state.audioplayer.isPlaying,
isAuthenticated: state.auth.loaded,
currentWord: state.ayahs.currentWord,
isEndOfSurah: ayahIds.size === surah.ayat,
surahs: state.surahs.entities,
bookmarks: state.bookmarks.entities,
isLoading: state.ayahs.loading,
isLoaded: state.ayahs.loaded,
lines: state.lines.lines,
Expand All @@ -451,7 +466,8 @@ function mapDispatchToProps(dispatch) {
options: bindActionCreators(OptionsActions, dispatch),
ayah: bindActionCreators(AyahActions, dispatch),
audio: bindActionCreators(AudioActions, dispatch),
push: bindActionCreators(push, dispatch)
push: bindActionCreators(push, dispatch),
bookmark: bindActionCreators(BookmarkActions, dispatch)
}
};
}
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/ApiClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default class {
}

if (data) {
request.send(data);
request.send(decamelizeKeys(data));
}

request.end((err, { body } = {}) => {
Expand Down
Loading

0 comments on commit b4b5bf5

Please sign in to comment.