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

Commit

Permalink
v2.1.0
Browse files Browse the repository at this point in the history
- added follows list screen
- added bookmark icon on manga list + manga screen
- added notifications for followed mangas releases
  • Loading branch information
Dastan21 committed May 21, 2021
1 parent 766ae9e commit 6bdd6e4
Show file tree
Hide file tree
Showing 16 changed files with 340 additions and 8,507 deletions.
53 changes: 42 additions & 11 deletions App.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@ import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import colors from './assets/styles/colors';
import styles from './assets/styles/styles';
import { HomeScreen, ChapterScreen, MangaScreen, AboutScreen, MangasListScreen } from './components/screens';
import secrets from './config/secrets';
import { HomeScreen, ChapterScreen, MangaScreen, AboutScreen, MangasScreen, FollowsScreen } from './components/screens';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { getFocusedRouteNameFromRoute } from '@react-navigation/native';
import { Notifications } from "expo/build/deprecated.web";
import AsyncStorage from '@react-native-async-storage/async-storage';
import { post } from 'axios';
const Tab = createBottomTabNavigator();
const Stack = createStackNavigator();

function getTabIcon(route, focused) {
let icon_image;
switch (route) {
case "MangasList": icon_image = focused ? require(`./assets/img/listfilled_tabicon.png`) : require(`./assets/img/list_tabicon.png`); break;
case "Mangas": icon_image = focused ? require(`./assets/img/listfilled_tabicon.png`) : require(`./assets/img/list_tabicon.png`); break;
case "Follows": icon_image = focused ? require(`./assets/img/bookmark_filled.png`) : require(`./assets/img/bookmark.png`); break;
case "About": icon_image = focused ? require(`./assets/img/infofilled_tabicon.png`) : require(`./assets/img/info_tabicon.png`); break;
default: icon_image = focused ? require(`./assets/img/homefilled_tabicon.png`) : require(`./assets/img/home_tabicon.png`);
}
Expand All @@ -32,19 +37,21 @@ function TabScreens({navigation, route}) {
case "About":
options = {
title: 'À PROPOS',
headerLeft: undefined
}
break;
case "MangasList":
case "Follows":
options = {
title: 'CHAPITRES SUIVIS',
};
break;
case "Mangas":
options = {
title: 'LISTE DES MANGAS',
headerLeft: undefined
};
break;
default:
options = {
title: 'LES DERNIÈRES SORTIES',
headerLeft: () => <Image style={styles.headerLeftImage} source={require('./assets/img/flamme.png')} />
title: 'DERNIERS CHAPITRES',
};
}
navigation.setOptions(options);
Expand All @@ -62,24 +69,48 @@ function TabScreens({navigation, route}) {

>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="MangasList" component={MangasListScreen} options={() => ({
title: 'Liste des mangas'
})} />
<Tab.Screen name="Follows" component={FollowsScreen} />
<Tab.Screen name="Mangas" component={MangasScreen} />
<Tab.Screen name="About" component={AboutScreen} options={{ title: 'À propos' }} />
</Tab.Navigator>
);
}

const App = () => {
useEffect(() => {
Image.resolveAssetSource({uri: './assets/img/flamme.png'});
Image.resolveAssetSource({uri: './assets/img/homefilled_tabicon.png'});
Image.resolveAssetSource({uri: './assets/img/home_tabicon.png'});
Image.resolveAssetSource({uri: './assets/img/listfilled_tabicon.png'});
Image.resolveAssetSource({uri: './assets/img/list_tabicon.png'});
Image.resolveAssetSource({uri: './assets/img/infofilled_tabicon.png'});
Image.resolveAssetSource({uri: './assets/img/info_tabicon.png'});
Image.resolveAssetSource({uri: './assets/img/bookmark_filled.png'});
Image.resolveAssetSource({uri: './assets/img/bookmark.png'});

initPushNotifs();
}, []);


const initPushNotifs = async () => {
let tokenShared;
try {
tokenShared = (await AsyncStorage.getItem('tokenShared')) === "true";
} catch (err) { console.error(err); }
if (!tokenShared) {
Notifications.getExpoPushTokenAsync().then(token => {
post(secrets.sf_api.url + "users/token", {
token: token
}, {
headers: { Authorization: `Bearer ${secrets.sf_api.token}` }
}).finally(async () => {
try {
await AsyncStorage.setItem('tokenShared', "true");
} catch (err) { console.error(err); }
});
}).catch(console.error);
}
};

return (
<NavigationContainer theme={{ colors: { background: colors.background }}}>
<StatusBar barStyle="light-content" hidden={true} animated={true} translucent={true} backgroundColor="transparent" />
Expand Down
10 changes: 5 additions & 5 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "ScantradFranceApp",
"slug": "ScantradFranceApp",
"privacy": "unlisted",
"version": "1.0.0",
"version": "2.1.0",
"orientation": "portrait",
"icon": "./assets/img/icon.png",
"splash": {
Expand All @@ -15,10 +15,10 @@
"hidden": true,
"barStyle": "light-content"
},
"androidNavigationBar": {
"visible": "immersive",
"barStyle": "light-content"
},
"androidNavigationBar": {
"visible": "sticky-immersive",
"barStyle": "light-content"
},
"primaryColor": "#F05A28",
"updates": {
"fallbackToCacheTimeout": 0
Expand Down
Binary file added assets/img/bookmark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/img/bookmark_filled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/img/flamme.png
Binary file not shown.
Binary file modified assets/img/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 23 additions & 1 deletion assets/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ export default StyleSheet.create({
height: 32,
marginLeft: 16
},
headerRightBookmark: {
width: 24,
height: 24,
marginRight: 16
},
headerRightText: {
fontWeight: 'bold',
marginRight: 16
Expand Down Expand Up @@ -99,7 +104,7 @@ export default StyleSheet.create({
},
chapterPreviewFullContainer: {
backgroundColor: colors.primary,
borderRadius: 4,
borderRadius: 4
},
chapterPreviewThumbnailBorder: {
borderRightWidth: 3,
Expand Down Expand Up @@ -145,6 +150,23 @@ export default StyleSheet.create({
right: 4,
bottom: 4
},
chapterPreviewBookmarkPressable: {

},
chapterPreviewBookmarkContainer: {
position: 'absolute',
width: 48,
height: 48,
right: 0,
top: 8,
},
chapterPreviewBookmarkIcon: {
position: 'absolute',
top: 6,
right: 0,
width: 24,
height: 24,
},
/**** Chapter ****/
chapterPageScreen: {
justifyContent: 'center',
Expand Down
28 changes: 28 additions & 0 deletions components/BannerHeader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import {
View,
Image
} from 'react-native';
import styles from "../assets/styles/styles";
import { useEffect } from 'react';


const BannerHeader = () => {

useEffect(() => {
Image.resolveAssetSource({ uri: '../assets/img/banner.png' });
}, []);

return (
<View style={styles.fullContainer}>
<Image
style={styles.banner}
source={require('../assets/img/banner.png')}
fadeDuration={0}
/>
</View>
);
}


module.exports = BannerHeader;
1 change: 0 additions & 1 deletion components/ChapterScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ const ChapterScreen = ({ navigation, route }) => {
break;
case "readstyle":
value ? setReadStyle("vertical") : setReadStyle("horizontal");
// if ((pages[0].number === 1) === value) setPages(pages.reverse());
break;
case "japread":
setJapRead(value);
Expand Down
1 change: 0 additions & 1 deletion components/ChapterScreen_save.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,6 @@ const ChapterScreen = ({ navigation, route }) => {
zoomStep={0.5}
initialZoom={1}
bindToBorders={true}
onDoubleTapBefore={(e, gest, obj) => console.log(gest, obj)}
>
<Pressable onLongPress={changeHeaderVisible}>
<Image
Expand Down
122 changes: 122 additions & 0 deletions components/FollowsScreen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import React, { useEffect, useState } from 'react';
import {
View,
Image,
RefreshControl,
Text,
FlatList,
TouchableHighlight
} from 'react-native';
import BackgroundImage from './BackgroundImage';
import LoadingScreen from './LoadingScreen';
import BannerHeader from './BannerHeader';
import styles from "../assets/styles/styles";
import secrets from '../config/secrets';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { get } from 'axios';

const MangaScreen = ({navigation}) => {

const [isLoadingChapters, setLoadingChapters] = useState(true);
const [chapters, setChapters] = useState([]);
const [refreshing, setRefreshing] = useState(false);

const getLastChapters = async limit => {
return get(secrets.sf_api.url + "chapters/" + limit, { headers: { Authorization: `Bearer ${secrets.sf_api.token}` } }).then(res => res.data).catch(console.error);
};
const loadChapters = () => {
getLastChapters(20)
.then(chaps => {
if (!chaps) return;
try {
AsyncStorage.getItem('follows').then(f => JSON.parse(f || "[]")).then(follows => {
setChapters(chaps.filter(c => follows.includes(c.manga.id)));
setLoadingChapters(false);
}).catch(console.error);
} catch (err) { console.error(err); }
}).catch(err => {
console.error(err);
setLoadingChapters(false);
});
};
const wait = timeout => {
return new Promise(resolve => {
setTimeout(resolve, timeout);
});
}
const onRefresh = () => {
setRefreshing(true);
wait(2000).then(() => {
setLoadingChapters(true);
loadChapters();
setRefreshing(false);
});
};

useEffect(() => {
loadChapters();
}, []);

if (isLoadingChapters)
return (<LoadingScreen />);
if (!chapters.length)
return (
<View>
<Text>
Une erreur s'est produite lors du chargement des chapitres...
</Text>
</View>
);
return (
<BackgroundImage>
<FlatList
style={styles.listChapters}
data={chapters}
renderItem={({ item }) => <ThumbnailChapter navigation={navigation} chapter={item} />}
keyExtractor={item => item.title}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
ListHeaderComponent={BannerHeader}
/>
</BackgroundImage>
);
}

const ThumbnailChapter = ({ navigation, chapter }) => {
const sliceText = (text, max) => {
if (text.length <= max) return [text, ""];
let t = text.split(' ');
let n = t[0].length, i = 0; while (i < t.length && n < max) { n += t[i].length + 1; i++; }
return [t.slice(0, i - 1).join(' '), t.slice(i - 1).join(' ')];
};

return (
<View style={styles.item}>
<TouchableHighlight style={styles.chapterPreviewFullContainer} onPress={() => navigation.navigate('Chapter', { chapter: chapter })}>
<View>
<Text>
<TouchableHighlight style={styles.chapterPreviewContainer} onPress={() => navigation.navigate('Manga', { manga: chapter.manga })}>
<View>
<Image style={styles.chapterPreviewThumbnail} source={{ uri: chapter.manga.thumbnail }} fadeDuration={0} />
<View style={styles.chapterPreviewThumbnailBorder} />
</View>
</TouchableHighlight>
<View style={styles.chapterPreviewContainer}>
<View style={{ flexWrap: 'nowrap' }}>
<Text style={[styles.text, styles.chapterPreviewName]}>{chapter.manga.name.slice(0, 28) + (chapter.manga.name.length > 28 ? "-" : "")}</Text>
</View>
<View>
<Text style={[styles.text, styles.chapterPreviewTitle]}>{sliceText(chapter.title, 43)[0]}</Text>
<Text style={[styles.text, styles.chapterPreviewTitle]}>{sliceText(chapter.title, 43)[1]}</Text>
</View>
</View>
</Text>
<Text style={[styles.text, styles.chapterPreviewNumber]}>{chapter.number}</Text>
<Text style={[styles.text, styles.chapterPreviewDate]}>{`Il y a ${chapter.release_date}`}</Text>
</View>
</TouchableHighlight>
</View>
);
};


module.exports = MangaScreen;
Loading

0 comments on commit 6bdd6e4

Please sign in to comment.