Browse Source

Android building

master
Pierre Bresson 3 years ago
parent
commit
f50a791e54
  1. 6
      android/app/build.gradle
  2. 2
      android/app/src/main/AndroidManifest.xml
  3. 2
      android/app/src/main/java/com/thinkerview/MainActivity.java
  4. 2
      android/app/src/main/java/com/thinkerview/MainApplication.java
  5. BIN
      android/app/src/main/res/mipmap-hdpi/ic_launcher.png
  6. BIN
      android/app/src/main/res/mipmap-ldpi/ic_launcher.png
  7. BIN
      android/app/src/main/res/mipmap-mdpi/ic_launcher.png
  8. BIN
      android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
  9. BIN
      android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  10. BIN
      android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  11. 2
      android/app/src/main/res/values/strings.xml
  12. 4
      app/actions/interviewsActions.js
  13. 3
      app/actions/offlineActions.js
  14. 4
      app/components/categoryModal.js
  15. 7
      app/components/displayNetwork.js
  16. 3
      app/components/labelCategory.js
  17. 7
      app/components/listItem/categoryItem.js
  18. 182
      app/components/listItem/podcastItem.js
  19. 135
      app/components/listItem/podcastItemOffline.js
  20. 129
      app/components/listItem/videoItem.js
  21. 60
      app/components/listItem/videoItemFeatured.js
  22. 5
      app/config/colors.js
  23. 2
      app/config/index.js
  24. 30
      app/config/strings.js
  25. 23
      app/config/urls.js
  26. 2
      app/reducers/categoriesReducer.js
  27. 4
      app/reducers/offlineReducer.js
  28. 23
      app/screens/about/index.js
  29. 10
      app/screens/home/article/index.js
  30. 21
      app/screens/home/index.js
  31. 4
      app/screens/offline/index.js
  32. 2
      app/screens/offline/podcast/index.js
  33. 53
      app/screens/podcast/index.js
  34. 2
      app/services/api/getAllCategories.js
  35. 31
      app/services/api/getInterviews.js
  36. 39
      app/services/api/getPodcasts.js
  37. 14
      app/services/staticServices/cleanWPjson.js
  38. 8
      app/services/staticServices/getYoutubeVideoID.js
  39. 10
      app/services/staticServices/getYoutubeVideoImageURL.js
  40. BIN
      assets/images/logo.png

6
android/app/build.gradle

@ -104,11 +104,11 @@ android {
}
defaultConfig {
applicationId "com.thinkerview"
applicationId "com.cause.commune"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 11
versionName "2.1.0"
versionCode 1
versionName "1.0.0"
ndk {
abiFilters "armeabi-v7a", "x86"
}

2
android/app/src/main/AndroidManifest.xml

@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.thinkerview">
package="com.cause.commune">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

2
android/app/src/main/java/com/thinkerview/MainActivity.java

@ -1,4 +1,4 @@
package com.thinkerview;
package com.cause.commune;
import com.facebook.react.ReactActivity;

2
android/app/src/main/java/com/thinkerview/MainApplication.java

@ -1,4 +1,4 @@
package com.thinkerview;
package com.cause.commune;
import android.app.Application;

BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
android/app/src/main/res/mipmap-ldpi/ic_launcher.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 15 KiB

BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 22 KiB

2
android/app/src/main/res/values/strings.xml

@ -1,3 +1,3 @@
<resources>
<string name="app_name">Thinkerview</string>
<string name="app_name">Cause Commune</string>
</resources>

4
app/actions/interviewsActions.js

@ -7,7 +7,7 @@ import {
INTERVIEWS_SCROLL_TO_TOP
} from "./types";
import { pathOr } from "ramda";
import getInterviews from "../services/api/getInterviews";
import getPodcasts from "../services/api/getPodcasts";
export const gettingInterviews = () => {
return {
@ -34,7 +34,7 @@ export const interviewsFetcher = (category_id = 0) => {
if (shouldFetch) {
dispatch(gettingInterviews());
getInterviews(getState().interviews.page, category_id)
getPodcasts(getState().interviews.page, category_id)
.then(res => {
return dispatch(gettingInterviewsSuccess(res));
})

3
app/actions/offlineActions.js

@ -233,12 +233,11 @@ export const deletePodcastOffline = podcast => {
};
export const updatePodcast = (id, key, value) => {
return async (dispatch, getState) => {
return (dispatch, getState) =>
dispatch({
type: SAVE_PODCAST_OFFLINE_UPDATE,
podcast: { id: Number(id) },
key,
value
});
};
};

4
app/components/categoryModal.js

@ -26,7 +26,9 @@ class CategoryModal extends React.Component {
}
renderItem = (item, index) => {
if (item.id === config.interview_category_id) return null;
if (item.id === config.interview_category_id) {
return null;
}
return (
<CategoryItem
item={item}

7
app/components/displayNetwork.js

@ -13,6 +13,7 @@ const GITHUB = "github";
const CREATIVE_COMMONS = "creative-commons";
const MASTODON = "mastodon";
const PEERTUBE = "video";
const INSTAGRAM = "instagram";
export default class DisplayNetwork extends React.Component {
constructor(props) {
@ -31,6 +32,8 @@ export default class DisplayNetwork extends React.Component {
return config.colors.socialMedia.mastodon;
case PEERTUBE:
return config.colors.socialMedia.peertube;
case INSTAGRAM:
return config.colors.socialMedia.instagram;
default:
return config.colors.blackTorn;
}
@ -63,11 +66,12 @@ export default class DisplayNetwork extends React.Component {
};
render() {
let {
const {
facebook_url,
twitter_url,
youtube_url,
website_url,
instagram_url,
paypal_url,
github_url,
creative_commons_url,
@ -80,6 +84,7 @@ export default class DisplayNetwork extends React.Component {
{this.renderItem(facebook_url, FACEBOOK)}
{this.renderItem(twitter_url, TWITTER)}
{this.renderItem(youtube_url, YOUTUBE)}
{this.renderItem(instagram_url, INSTAGRAM)}
{this.renderItem(website_url, LINK)}
{this.renderItem(paypal_url, PAYPAL)}
{this.renderItem(github_url, GITHUB)}

3
app/components/labelCategory.js

@ -15,8 +15,7 @@ const styles = StyleSheet.create({
paddingTop: 4,
paddingBottom: 6,
paddingLeft: 10,
paddingRight: 10,
borderRadius: 20
paddingRight: 10
},
text: {
color: "white",

7
app/components/listItem/categoryItem.js

@ -10,10 +10,13 @@ export default class CategoryItem extends React.Component {
render() {
let { item, onPress, style } = this.props;
let { name, id } = item;
let { name } = item;
if (!name || id == 1) return null;
if (!name) return null;
if (item.id === config.interview_category_id) {
name = config.strings.categoryModal.allCategories;
}
return (
<ReactNative.TouchableOpacity
style={[styles.container, style]}

182
app/components/listItem/podcastItem.js

@ -0,0 +1,182 @@
import React from "react";
import { TouchableOpacity, View, Text, Image, StyleSheet } from "react-native";
import IconEntypo from "react-native-vector-icons/Entypo";
import IconMaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
import { connect } from "react-redux";
import TrackPlayer from "react-native-track-player";
import { updateTrackInfo, savePodcastOffline } from "../../actions";
import LabelCategory from "../labelCategory";
import config from "../../config";
import _ from "lodash";
class PodcastItem extends React.Component {
constructor(props) {
super(props);
}
renderCategories = categories => {
const { all_categories } = this.props.categories;
let categories_name = [];
if (all_categories && categories) {
if (all_categories.length && categories.length) {
categories.map(category => {
category_found = all_categories.filter(cat => category === cat.id);
if (category_found)
if (category_found[0].name != "Interviews")
categories_name.push(category_found[0].name);
});
}
}
if (categories_name)
if (categories_name.length)
return categories_name.map(category_name => (
<LabelCategory key={category_name} category={category_name} />
));
};
getImage = categories => {
const { all_categories } = this.props.categories;
if (all_categories && categories) {
if (all_categories.length && categories.length) {
const category_found = all_categories.filter(
cat => categories[0] === cat.id
);
if (category_found) {
return category_found[0].image;
}
}
}
};
savedPodcast = () => {
let item = this.props.item;
item.img_url = this.getImage(item.categories);
this.props.savePodcastOffline(item);
this.props.navigation.navigate("Offline");
};
listenPodcast = async () => {
let { item } = this.props;
let { title, audio_link, categories } = item;
const img_url = this.getImage(categories);
TrackPlayer.reset();
await TrackPlayer.add({
id: audio_link,
url: audio_link,
title: title,
artist: "Cause Commune",
album: "Podcast",
artwork: img_url
});
await TrackPlayer.play();
let info = {
title: title,
url: audio_link,
artwork: img_url
};
this.props.updateTrackInfo(info);
this.props.navigation.navigate("Podcast");
};
renderSave = () => (
<TouchableOpacity style={styles.controlBtn} onPress={this.savedPodcast}>
<IconEntypo name={"download"} size={40} color={config.colors.blackTorn} />
<Text style={styles.textSmall}>{config.strings.videoItem.save}</Text>
</TouchableOpacity>
);
renderListen = () => (
<TouchableOpacity style={styles.controlBtn} onPress={this.listenPodcast}>
<IconMaterialCommunityIcons
name={"play-circle"}
size={40}
color={config.colors.blackTorn}
/>
<Text style={styles.textSmall}>{config.strings.videoItem.listen}</Text>
</TouchableOpacity>
);
render() {
let { item, style } = this.props;
let { title, audio_link, categories } = item;
const img_url = this.getImage(categories);
if (!title || !audio_link || !img_url) return null;
return (
<View style={[styles.container, style]}>
<Image
style={styles.img}
resizeMode="cover"
source={{ uri: img_url }}
/>
<View style={styles.textView}>
<Text numberOfLines={3} style={styles.text}>
{_.capitalize(title)}
</Text>
<View style={styles.categoriesView}>
{/* {this.renderCategories(categories)} */}
{this.renderListen()}
{this.renderSave()}
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flexDirection: "row",
paddingTop: 14,
paddingBottom: 14,
paddingLeft: 20,
borderBottomWidth: 0.5,
borderBottomColor: config.colors.silverTwo
},
img: {
height: 160,
width: 160
},
textView: {
flexDirection: "column",
flex: 1,
marginLeft: 10,
marginRight: 10
},
text: {
fontSize: 18,
fontFamily: config.fonts.bold,
color: config.colors.black
},
textSmall: {
marginTop: 10,
marginLeft: 4,
fontSize: 16,
fontFamily: config.fonts.bodyFont,
color: config.colors.black
},
categoriesView: {
flexDirection: "column",
marginTop: 14
},
controlBtn: {
flexDirection: "row"
}
});
const mapStateToProps = state => {
return { categories: state.categories };
};
const mapDispatchToProps = dispatch => {
return {
savePodcastOffline: podcast => dispatch(savePodcastOffline(podcast)),
updateTrackInfo: info => dispatch(updateTrackInfo(info))
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(PodcastItem);

135
app/components/listItem/podcastItemOffline.js

@ -0,0 +1,135 @@
import React from "react";
import { View, TouchableOpacity, Text, Image, StyleSheet } from "react-native";
import IconMaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
import { connect } from "react-redux";
import { updateTrackInfo, savePodcastOffline } from "../../actions";
import config from "../../config";
import _ from "lodash";
class PodcastItemOffline extends React.Component {
constructor(props) {
super(props);
}
getImage = categories => {
const { all_categories } = this.props.categories;
if (all_categories && categories) {
if (all_categories.length && categories.length) {
const category_found = all_categories.filter(
cat => categories[0] === cat.id
);
if (category_found) {
return category_found[0].image;
}
}
}
};
renderListen = () => (
<TouchableOpacity style={styles.controlBtn} onPress={this.listenPodcast}>
<IconMaterialCommunityIcons
name={"play-circle"}
size={40}
color={config.colors.blackTorn}
/>
<Text style={styles.textSmall}>{config.strings.videoItem.listen}</Text>
</TouchableOpacity>
);
render() {
let { item, style, onPress } = this.props;
let { title, audio_link, categories } = item;
const img_url = this.getImage(categories);
if (!title || !audio_link || !img_url) return null;
return (
<TouchableOpacity style={[styles.container, style]} onPress={onPress}>
<Image
style={styles.img}
resizeMode="cover"
source={{ uri: img_url }}
/>
<View style={styles.smallContainer}>
<View style={styles.textView}>
<Text numberOfLines={5} style={styles.text}>
{_.capitalize(title)}
</Text>
</View>
<View style={styles.iconChevron}>
<IconMaterialCommunityIcons
name={"chevron-right"}
size={30}
color={config.colors.blackTorn}
/>
</View>
</View>
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
container: {
flexDirection: "row",
paddingTop: 14,
paddingBottom: 14,
paddingLeft: 20,
borderBottomWidth: 0.5,
borderBottomColor: config.colors.silverTwo
},
img: {
height: 100,
width: 100
},
smallContainer: {
flex: 1,
flexDirection: "row"
},
textView: {
flex: 6,
marginLeft: 10,
marginRight: 10
},
iconChevron: {
flex: 1,
justifyContent: "center",
alignItems: "center",
marginLeft: 2,
marginRight: 2
},
text: {
fontSize: 18,
fontFamily: config.fonts.bold,
color: config.colors.black
},
textSmall: {
marginTop: 10,
marginLeft: 4,
fontSize: 16,
fontFamily: config.fonts.bodyFont,
color: config.colors.black
},
categoriesView: {
flexDirection: "column",
marginTop: 14
},
controlBtn: {
flexDirection: "row"
}
});
const mapStateToProps = state => {
return { categories: state.categories };
};
const mapDispatchToProps = dispatch => {
return {
savePodcastOffline: podcast => dispatch(savePodcastOffline(podcast)),
updateTrackInfo: info => dispatch(updateTrackInfo(info))
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(PodcastItemOffline);

129
app/components/listItem/videoItem.js

@ -1,129 +0,0 @@
import React from "react";
import {
TouchableOpacity,
View,
Text,
Image,
StyleSheet,
ScrollView
} from "react-native";
import { connect } from "react-redux";
import LabelCategory from "../labelCategory";
import config from "../../config";
import _ from "lodash";
class VideoItem extends React.Component {
constructor(props) {
super(props);
}
renderCategories = categories => {
const { all_categories } = this.props.categories;
let categories_name = [];
if (all_categories && categories) {
if (all_categories.length && categories.length) {
categories.map(category => {
category_found = all_categories.filter(cat => category === cat.id);
if (category_found)
if (category_found[0])
if (category_found[0].name != "Interviews")
categories_name.push(category_found[0].name);
});
}
}
if (categories_name)
if (categories_name.length)
return categories_name.map(category_name => (
<LabelCategory key={category_name} category={category_name} />
));
};
// renderImage = () => {
// let { img_url, image_offline } = this.props.item;
// if (image_offline) {
// return (
// <Image
// style={styles.img}
// resizeMode="cover"
// source={require(`${image_offline}`)}
// />
// );
// } else {
// return (
// <Image
// style={styles.img}
// resizeMode="cover"
// source={{ uri: img_url }}
// />
// );
// }
// };
render() {
let { item, onPress, style } = this.props;
let { title, video_id, categories, img_url } = item;
if (!title || !video_id || !img_url) return null;
return (
<TouchableOpacity style={[styles.container, style]} onPress={onPress}>
<Image
style={styles.img}
resizeMode="cover"
source={{ uri: img_url }}
/>
<View style={styles.textView}>
<Text numberOfLines={2} style={styles.text}>
{_.capitalize(title)}
</Text>
<View style={styles.categoriesView}>
{this.renderCategories(categories)}
</View>
</View>
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
container: {
flexDirection: "row",
paddingTop: 14,
paddingBottom: 14,
paddingLeft: 20,
borderBottomWidth: 0.5,
borderBottomColor: config.colors.silverTwo
},
img: {
height: 76,
width: 134
},
textView: {
flexDirection: "column",
flex: 1,
marginLeft: 10,
marginRight: 10
},
text: {
fontSize: 16,
fontFamily: config.fonts.bold,
color: config.colors.black
},
categoriesView: {
flexDirection: "row",
marginTop: 14
}
});
const mapStateToProps = state => {
return { categories: state.categories };
};
const mapDispatchToProps = dispatch => {
return {};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(VideoItem);

60
app/components/listItem/videoItemFeatured.js

@ -1,60 +0,0 @@
import React from "react";
import { TouchableOpacity, View, Text, Image, StyleSheet } from "react-native";
import config from "../../config";
import _ from "lodash";
export default (VideoItemFeatured = ({ item, onPress, style }) => {
let { title, img_url, video_id } = item;
if (!title || !video_id || !img_url) return null;
return (
<TouchableOpacity style={[styles.container, style]} onPress={onPress}>
<View style={styles.imgView}>
<Image
style={styles.img}
resizeMode="cover"
source={{ uri: img_url }}
/>
</View>
<View style={styles.textView}>
<Text numberOfLines={2} style={styles.text}>
{_.capitalize(title)}
</Text>
</View>
</TouchableOpacity>
);
});
const styles = StyleSheet.create({
container: {
flexDirection: "column",
paddingBottom: 14,
paddingLeft: 20,
paddingRight: 20,
borderBottomWidth: 0.5,
borderBottomColor: config.colors.silverTwo
},
imgView: {
flex: 1,
justifyContent: "center",
alignItems: "center",
marginBottom: 14,
height: 200
},
img: {
position: "absolute",
top: 0,
left: 0,
bottom: 0,
right: 0
},
textView: {
flex: 1
},
text: {
fontSize: 20,
fontFamily: config.fonts.bold,
color: config.colors.black
}
});

5
app/config/colors.js

@ -5,13 +5,14 @@ const colors = {
backgroundColor: "#fef7f9",
blackTorn: "#333333",
silverTwo: "#c8c7cc",
thinkerGreen: "#45be84",
thinkerGreen: "#E63059",
socialMedia: {
facebook: "#3b5999",
twitter: "#55acee",
youtube: "#cd201f",
mastodon: "#469CDA",
peertube: "#F07436"
peertube: "#F07436",
instagram: "#c32aa3"
}
};

2
app/config/index.js

@ -7,7 +7,7 @@ import urls from "./urls";
import privateKeys from "./privateKeys";
export default {
interview_category_id: 9,
interview_category_id: 0,
articlesPerPage: "25",
icons: icons,
images: images,

30
app/config/strings.js

@ -10,23 +10,19 @@ const strings = {
copyLink: "Copier le lien"
},
homeScreen: {
endOfList: "The end!",
check_website_message: "Peut être que le site Thinkerview ne répond plus.",
check_website_button: "Vérifier état du site"
endOfList: "The end!"
},
offlineScreen: {
title: "Podcasts hors-ligne",
nothingToShow: "Télécharger un podcast pour qu'il s'affiche ici."
nothingToShow: "Enregistrer une émission et elle s'affichera ici."
},
aboutScreen: {
about: "À propos",
aboutAuthor:
"Cette application a été conçu par Pierre Bresson, bénévolement. N'hésitez pas à me faire parvenir vos bugs et la procédure pour les reproduire, de façon civilisée. Si l'application n'arrive pas à charger les interviews, c'est peut-être que le site de Thinkerview ne marche pas et dans ce cas je ne peux malheureusement rien faire.",
thinkerview: "Thinkerview",
thinkerviewDescription:
"ThinkerView est un groupe indépendant issu d'internet, très diffèrent de la plup" +
"art des think-tanks qui sont inféodés à des partis politiques ou des intérêts pr" +
"ivés.",
"Cette application a été conçu par Pierre Bresson, bénévolement. N'hésitez pas à me faire parvenir vos bugs et la procédure pour les reproduire, de façon civilisée. Si l'application n'arrive pas à charger les interviews, c'est peut-être que le site de Cause Commune ne marche et dans ce cas je ne peux malheureusement rien faire.",
cause_commune: "Cause Commune - 93.1 FM",
cause_commune_description:
"Cause Commune rassemble dans sa grille de programmes les voix pour l’instant disparates des chercheurs et des inventeurs de solutions propres à relever les défis écologiques, techniques, sociaux et économiques du monde d’aujourd’hui. Pour ce faire, sont notamment invités à la rejoindre tous les acteurs du logiciel libre et du numérique, de la culture libre, de la science et de l’éducation, de l’environnement et de la nature qui oeuvrent pour le maintien et la sauvegarde des Biens Communs et pour une société de la Connaissance fondée sur le partage.",
licence: "Licence",
licenceDescription:
"Les vidéos de Thinkerview sont mises à disposition selon les termes de la Licenc" +
@ -34,7 +30,8 @@ const strings = {
"s Mêmes Conditions 4.0 International."
},
categoryModal: {
header: "Catégories"
header: "Émissions",
allCategories: "Toutes les émissions"
},
articleScreen: {
playVideo: "Lire la vidéo",
@ -46,11 +43,18 @@ const strings = {
noYoutube: "Merci d'installer Youtube pour pouvoir lire la vidéo."
},
podcastScreen: {
header: "Podcast",
title: "Aucun podcast sélectionné"
header: "Ecouter en direct",
title: "Aucune séléction"
},
headerComponent: {
back: "Retour"
},
videoItem: {
listen: "Ecouter",
save: "Enregistrer",
alertTitle: "Attention",
alertMessage:
"Voulez-vous lancer le téléchargement du podcast afin de pouvoir l'écouter en mode hors ligne? Si oui, ne pas interrompre le téléchargement sous peine de voir le téléchargement se bloquer."
}
};

23
app/config/urls.js

@ -1,11 +1,10 @@
const urls = {
api: {
podcast_download: "https://thinkerview.com/podcast-download/",
base_url: "https://thinkerview.com/wp-json/wp/v2/",
all_categories: "categories?per_page=100",
category: "&categories=",
base_url: "https://cause-commune.fm/wp-json/wp/v2/",
all_series: "series?per_page=50",
series: "&series=",
per_page: "&per_page=",
page: "posts?page="
page: "podcast?page="
},
links: {
author: {
@ -13,14 +12,12 @@ const urls = {
website: "http://pierrebresson.com",
github: "https://github.com/PierreBresson"
},
thinkerview: {
facebook: "https://facebook.com/Thinkerview",
twitter: "https://twitter.com/Thinker_View",
youtube: "https://youtube.com/Thinkerview",
website: "https://thinkerview.com",
tipeee: "https://www.tipeee.com/thinkerview",
mastodon: "https://mamot.fr/@thinkerview",
peertube: "https://thinkerview.video/"
cause_commune: {
facebook: "https://www.facebook.com/causecommune93.1/",
twitter: "https://twitter.com/_CauseCommune_",
instagram: "https://www.instagram.com/causecommune93.1/",
website: "https://cause-commune.fm/",
don: "https://cause-commune.fm/faire-un-don/"
},
licence: {
creative_commons: "https://creativecommons.org/licenses/by-nc-sa/4.0/"

2
app/reducers/categoriesReducer.js

@ -8,7 +8,7 @@ import {
const initialcategorySelected = {
id: 0,
name: "Toutes les interviews"
name: "Toutes les émissions"
};
const initialState = {
isFetchingCategories: false,

4
app/reducers/offlineReducer.js

@ -39,7 +39,7 @@ export default (offlineReducer = (state = initialState, action) => {
if (!findPodcast(state.data, action.podcast.id)) {
return {
...state,
data: [{ ...action.podcast, progress: "0" }, ...state.data]
data: [{ ...action.podcast, progress: 0 }, ...state.data]
};
} else {
return { ...state };
@ -47,7 +47,7 @@ export default (offlineReducer = (state = initialState, action) => {
} else {
return {
...state,
data: [{ ...action.podcast, progress: "0" }]
data: [{ ...action.podcast, progress: 0 }]
};
}
case DELETE_PODCAST_OFFLINE:

23
app/screens/about/index.js

@ -22,28 +22,23 @@ export default class AboutScreen extends React.Component {
<View style={styles.subHeaderView}>
<Text style={styles.subHeader}>
{config.strings.aboutScreen.thinkerview}
{config.strings.aboutScreen.cause_commune}
</Text>
</View>
<Text style={styles.body}>
{config.strings.aboutScreen.thinkerviewDescription}
{config.strings.aboutScreen.cause_commune_description}
</Text>
<DisplayNetwork
facebook_url={config.urls.links.thinkerview.facebook}
twitter_url={config.urls.links.thinkerview.twitter}
youtube_url={config.urls.links.thinkerview.youtube}
website_url={config.urls.links.thinkerview.website}
paypal_url={config.urls.links.thinkerview.tipeee}
facebook_url={config.urls.links.cause_commune.facebook}
twitter_url={config.urls.links.cause_commune.twitter}
instagram_url={config.urls.links.cause_commune.instagram}
website_url={config.urls.links.cause_commune.website}
paypal_url={config.urls.links.cause_commune.don}
/>
<DisplayNetwork
mastodon_url={config.urls.links.thinkerview.mastodon}
peertube_url={config.urls.links.thinkerview.peertube}
/>
<View style={styles.subHeaderView}>
{/* <View style={styles.subHeaderView}>
<Text style={styles.subHeader}>
{config.strings.aboutScreen.licence}
</Text>
@ -55,7 +50,7 @@ export default class AboutScreen extends React.Component {
<DisplayNetwork
creative_commons_url={config.urls.links.licence.creative_commons}
/>
/> */}
</ScrollView>
);
}

10
app/screens/home/article/index.js

@ -194,14 +194,10 @@ class ArticleScreen extends React.Component {
onPressRight={() => this.props.shareSocialAction()}
/>
<View style={[config.styles.container, { height: "100%" }]}>
{this.renderVideoAndroid(img_url, video_id)}
{this.renderVideoIOS(video_id)}
{this.renderAudio(audio_link, img_url, title)}
{this.renderOffLine()}
<Text style={styles.header}>{_.capitalize(title)}</Text>
<Text style={styles.body}>{_.capitalize(body)}</Text>
</View>
</ScrollView>
<ShareSocial />
@ -212,11 +208,11 @@ class ArticleScreen extends React.Component {
const styles = StyleSheet.create({
img: {
height: 100,
width: 200
height: 250,
width: 250
},
header: {
paddingTop: Platform.OS === "ios" ? 20 : 0,
marginTop: 20,
fontSize: 20,
color: config.colors.black,
fontFamily: config.fonts.black

21
app/screens/home/index.js

@ -22,8 +22,7 @@ import {
} from "../../actions";
import IconEntypo from "react-native-vector-icons/Entypo";
import Button from "../../components/button";
import VideoItem from "../../components/listItem/videoItem";
import VideoItemFeatured from "../../components/listItem/videoItemFeatured";
import PodcastItem from "../../components/listItem/podcastItem";
import CategoryModal from "../../components/categoryModal";
import config from "../../config";
@ -49,8 +48,8 @@ class HomeScreen extends React.Component {
const { isFetchingCategories } = this.props.categories;
if (!isFetchingCategories && !isFetchingInterviews) {
this.props.interviewsFetcher(this.props.categories.categorySelected.id);
this.props.categoriesFetcher();
this.props.interviewsFetcher(this.props.categories.categorySelected.id);
}
};
@ -107,19 +106,9 @@ class HomeScreen extends React.Component {
return null;
};
renderItem = (item, index) => {
const VideoComponent = index ? VideoItem : VideoItemFeatured;
return (
<VideoComponent
item={item}
onPress={() => {
this.props.selectArticle(item);
this.props.navigation.navigate("Article");
}}
/>
);
};
renderItem = (item, index) => (
<PodcastItem item={item} navigation={this.props.navigation} />
);
renderFooter = ({ section }) => {
const { isFetchingInterviews, lastPage } = this.props.interviews;

4
app/screens/offline/index.js

@ -3,7 +3,7 @@ import { FlatList, Text, View, ScrollView, StyleSheet } from "react-native";
import { connect } from "react-redux";
import RNBackgroundDownloader from "react-native-background-downloader";
import { selectOfflinePodcast, updatePodcast } from "../../actions";
import VideoItem from "../../components/listItem/videoItem";
import PodcastItemOffline from "../../components/listItem/podcastItemOffline";
import config from "../../config";
class OfflineScreen extends React.Component {
@ -56,7 +56,7 @@ class OfflineScreen extends React.Component {
data={this.props.offline.data}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => (
<VideoItem
<PodcastItemOffline
key={item.id}
item={item}
onPress={() => {

2
app/screens/offline/podcast/index.js

@ -130,7 +130,7 @@ class OfflinePodcastScreen extends React.Component {
return (
<Image
style={styles.img}
resizeMode="cover"
resizeMode="contain"
source={{ uri: image_offline ? image_offline : img_url }}
/>
);

53
app/screens/podcast/index.js

@ -1,7 +1,9 @@
import React, { Component } from "react";
import { Image, Text, View, StyleSheet } from "react-native";
import { Image, Text, TouchableOpacity, View, StyleSheet } from "react-native";
import { connect } from "react-redux";
import TrackPlayer from "react-native-track-player";
import IconMaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
import { updateTrackInfo } from "../../actions";
import PlayerButton from "../../components/playerButton";
import ProgressBar from "../../components/progressBar";
import config from "../../config";
@ -19,11 +21,35 @@ class PodcastScreen extends Component {
}
};
renderIntro = () => {
_listenLive = async () => {
TrackPlayer.reset();
let info = {
title: "Live Cause Commune 93.1",
artwork: null
};
await TrackPlayer.add({
id: "live",
url: "https://icecast.libre-a-toi.org:8444/voixdulat_mp3",
title: "Cause Commune en direct sur 93.1",
artist: "Cause Commune",
album: "Podcast"
});
this.props.updateTrackInfo(info);
await TrackPlayer.play();
};
renderLive = () => {
return (
<View style={styles.headerView}>
<Text style={styles.header}>{config.strings.podcastScreen.header}</Text>
</View>
<TouchableOpacity style={styles.headerView} onPress={this._listenLive}>
<Text style={styles.headerText}>
{config.strings.podcastScreen.header}
</Text>
<IconMaterialCommunityIcons
name={"radio-tower"}
size={35}
color={config.colors.thinkerGreen}
/>
</TouchableOpacity>
);
};
@ -67,7 +93,7 @@ class PodcastScreen extends Component {
let { artwork } = this.props.track;
return (
<View style={config.styles.container}>
{this.renderIntro()}
{this.renderLive()}
<View style={{ flex: 1 }}>
<Image
style={styles.artwork}
@ -87,12 +113,14 @@ class PodcastScreen extends Component {
const styles = StyleSheet.create({
headerView: {
flexDirection: "row",
paddingTop: 40,
paddingBottom: 20,
alignItems: "center"
},
header: {
headerText: {
fontSize: 30,
paddingRight: 10,
fontFamily: config.fonts.titleFont,
color: config.colors.black
},
@ -128,4 +156,13 @@ function mapStateToProps(state) {
};
}
module.exports = connect(mapStateToProps)(PodcastScreen);
const mapDispatchToProps = dispatch => {
return {
updateTrackInfo: info => dispatch(updateTrackInfo(info))
};
};
module.exports = connect(
mapStateToProps,
mapDispatchToProps
)(PodcastScreen);

2
app/services/api/getAllCategories.js

@ -2,7 +2,7 @@ import axios from "axios";
import config from "../../config";
export default (getAllCategories = () => {
let url = config.urls.api.base_url + config.urls.api.all_categories;
let url = config.urls.api.base_url + config.urls.api.all_series;
let promiseGetAllCategories = new Promise((resolve, reject) => {
axios({
method: "get",

31
app/services/api/getInterviews.js

@ -1,31 +0,0 @@
import axios from "axios";
import config from "../../config";
import cleanWPjson from "../../services/staticServices/cleanWPjson";
export default (getInterviews = (page, category_id) => {
const category = category_id ? category_id : config.interview_category_id;
let url =
config.urls.api.base_url +
config.urls.api.page +
page +
config.urls.api.category +
category +
config.urls.api.per_page +
config.articlesPerPage;
let promiseGetInterviews = new Promise((resolve, reject) => {
axios({
method: "get",
url,
timeout: 3000
})
.then(res => {
resolve(cleanWPjson(res.data));
})
.catch(err => {
reject(err);
});
});
return promiseGetInterviews;
});

39
app/services/api/getPodcasts.js

@ -0,0 +1,39 @@
import axios from "axios";
import config from "../../config";
import cleanWPjson from "../staticServices/cleanWPjson";
export default (getPodcasts = (page, category_id) => {
let url =
config.urls.api.base_url +
config.urls.api.page +
page +
config.urls.api.per_page +
config.articlesPerPage;
if (category_id) {
url =
config.urls.api.base_url +
config.urls.api.page +
page +
config.urls.api.series +
category_id +
config.urls.api.per_page +
config.articlesPerPage;
}
let promiseGetPodcasts = new Promise((resolve, reject) => {
axios({
method: "get",
url,
timeout: 3000
})
.then(res => {
resolve(cleanWPjson(res.data));
})
.catch(err => {
reject(err);
});
});
return promiseGetPodcasts;
});

14
app/services/staticServices/cleanWPjson.js

@ -1,26 +1,16 @@
var he = require("he");
import getYoutubeVideoImageURL from "./getYoutubeVideoImageURL";
import getYoutubeVideoID from "./getYoutubeVideoID";
import config from "../../config";
export default (cleanWPjson = json => {
let cleanJSON = [];
json.map(json_item => {
let item = {
id: json_item.id,
categories: json_item.categories,
categories: json_item.series,
title: he.unescape(json_item.title.rendered),
body: he.unescape(
json_item.excerpt.rendered.replace(/<\/?[^>]+(>|$)/g, "")
),
img_url: getYoutubeVideoImageURL(json_item.acf.youtube),
video_id: getYoutubeVideoID(json_item.acf.youtube),
audio_link:
config.urls.api.podcast_download +
json_item.id +
"/" +
json_item.slug +
".mp3?ref=appthk"
audio_link: json_item.meta.audio_file
};
cleanJSON.push(item);
});

8
app/services/staticServices/getYoutubeVideoID.js

@ -1,8 +0,0 @@
export default getYoutubeVideoID = (youtubeVideoURL='') => {
const regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/;
const match = youtubeVideoURL.match(regExp);
if (match)
<