Merge branch 'develop'

This commit is contained in:
dario-cfpt
2020-03-17 13:46:02 +01:00
55 changed files with 1277 additions and 65 deletions

View File

@ -1,8 +1,11 @@
# FE_Charts
Fire Emblem Charts is a mobile application that allows you to view the statistics of the characters in the game Fire Emblem Three Houses in the form of graphics.
## Setup the server
## Setup the app
Copy the `.env-example` file and rename it to `.env`, then set the value of each variables corresponding to your environments.
- Change the variables in the `database.php` file to match the server.
- Change the `BASE_URL` constant in the `database.js` file with the host corresponding to the server.
Install the dependencies with `npm install`, then run the server with `npm start`
## Updating the catalogue
After updating the catalogue, you must add a new row in the `Tbl_Catalogue` table with the new version of the catalogue and the date of the update.

View File

@ -20,9 +20,44 @@
<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application">
<application android:usesCleartextTraffic="true" />
</edit-config>
<icon density="ldpi" src="icons/android/Icon-36.png" />
<icon density="ldpi" src="icons/android/Icon-48.png" />
<icon density="ldpi" src="icons/android/Icon-72.png" />
<icon density="ldpi" src="icons/android/Icon-96.png" />
<icon density="ldpi" src="icons/android/Icon-144.png" />
<icon density="ldpi" src="icons/android/Icon-192.png" />
<splash src="res/screen/android/splash-port-ldpi.jpg" density="port-ldpi"/>
<splash src="res/screen/android/splash-land-ldpi.jpg" density="land-ldpi"/>
<splash src="res/screen/android/splash-port-mdpi.jpg" density="port-mdpi"/>
<splash src="res/screen/android/splash-land-mdpi.jpg" density="land-mdpi"/>
<splash src="res/screen/android/splash-port-hdpi.jpg" density="port-hdpi"/>
<splash src="res/screen/android/splash-land-hdpi.jpg" density="land-hdpi"/>
<splash src="res/screen/android/splash-port-xhdpi.jpg" density="port-xhdpi"/>
<splash src="res/screen/android/splash-land-xhdpi.jpg" density="land-xhdpi"/>
<splash src="res/screen/android/splash-port-xxhdpi.jpg" density="port-xxhdpi"/>
<splash src="res/screen/android/splash-land-xxhdpi.jpg" density="land-xxhdpi"/>
<splash src="res/screen/android/splash-port-xxxhdpi.jpg" density="port-xxxhdpi"/>
<splash src="res/screen/android/splash-land-xxxhdpi.jpg" density="land-xxxhdpi"/>
</platform>
<platform name="ios">
<allow-intent href="itms:*" />
<allow-intent href="itms-apps:*" />
<icon src="icons/ios/Icon-29.png" height="29" width="29" />
<icon src="icons/ios/Icon-40.png" height="40" width="40" />
<icon src="icons/ios/Icon-50.png" height="50" width="50" />
<icon src="icons/ios/Icon-57.png" height="57" width="57" />
<icon src="icons/ios/Icon-58.png" height="58" width="58" />
<icon src="icons/ios/Icon-60.png" height="60" width="60" />
<icon src="icons/ios/Icon-72.png" height="72" width="72" />
<icon src="icons/ios/Icon-76.png" height="76" width="76" />
<icon src="icons/ios/Icon-80.png" height="80" width="80" />
<icon src="icons/ios/Icon-100.png" height="100" width="100" />
<icon src="icons/ios/Icon-114.png" height="114" width="114" />
<icon src="icons/ios/Icon-120.png" height="120" width="120" />
<icon src="icons/ios/Icon-144.png" height="144" width="144" />
<icon src="icons/ios/Icon-152.png" height="152" width="152" />
<icon src="icons/ios/Icon-167.png" height="167" width="167" />
<icon src="icons/ios/Icon-172.png" height="172" width="172" />
<icon src="icons/ios/Icon-180.png" height="180" width="180" />
</platform>
</widget>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -274,6 +274,16 @@
"resolved": "https://registry.npmjs.org/cordova-plugin-device/-/cordova-plugin-device-2.0.3.tgz",
"integrity": "sha512-Jb3V72btxf3XHpkPQsGdyc8N6tVBYn1vsxSFj43fIz9vonJDUThYPCJJHqk6PX6N4dJw6I4FjxkpfCR4LDYMlw=="
},
"cordova-plugin-network-information": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/cordova-plugin-network-information/-/cordova-plugin-network-information-2.0.2.tgz",
"integrity": "sha512-NwO3qDBNL/vJxUxBTPNOA1HvkDf9eTeGH8JSZiwy1jq2W2mJKQEDBwqWkaEQS19Yd/MQTiw0cykxg5D7u4J6cQ=="
},
"cordova-plugin-splashscreen": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/cordova-plugin-splashscreen/-/cordova-plugin-splashscreen-5.0.3.tgz",
"integrity": "sha512-rnoDXMDfzoeHDBvsnu6JmzDE/pV5YJCAfc5hYX/Mb2BIXGgSjFJheByt0tU6kp3Wl40tSyFX4pYfBwFblBGyRg=="
},
"cordova-plugin-whitelist": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/cordova-plugin-whitelist/-/cordova-plugin-whitelist-1.3.4.tgz",

View File

@ -16,7 +16,9 @@
"cordova-android": "^8.1.0",
"cordova-browser": "^6.0.0",
"cordova-ios": "^5.1.1",
"cordova-plugin-device": "^2.0.3"
"cordova-plugin-device": "^2.0.3",
"cordova-plugin-network-information": "^2.0.2",
"cordova-plugin-splashscreen": "^5.0.3"
},
"devDependencies": {
"cordova-plugin-whitelist": "^1.3.4"
@ -24,7 +26,9 @@
"cordova": {
"plugins": {
"cordova-plugin-whitelist": {},
"cordova-plugin-device": {}
"cordova-plugin-device": {},
"cordova-plugin-network-information": {},
"cordova-plugin-splashscreen": {}
},
"platforms": [
"android",

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

View File

@ -20,4 +20,29 @@
.data-table-title {
text-align: center;
}
.text-center {
text-align: center;
}
.logo {
display: block;
margin-left: auto;
margin-right: auto;
}
h1.about, h2.about, h3.about {
color: #1f3487;
margin-top: 0px;
}
.item-media > img {
width: 32px;
height: 32px;
margin-left: -20px;
}
.item-checkbox > .item-inner {
margin-left: -10px !important;
}

BIN
mobile/www/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

@ -104,13 +104,13 @@
<!-- Your custom app scripts -->
<script src="js/app.js"></script>
<script src="js/index.js"></script>
<script src="js/database.js"></script>
<script src="js/graph.js"></script>
<script src="js/feclass.js"></script>
<script src="js/stats.js"></script>
<script src="js/feclass-stats.js"></script>
<script src="js/comparator.js"></script>
<script src="js/index.js"></script>
</body>
</html>

View File

@ -16,9 +16,9 @@ var app = new Framework7({
// App routes
routes: routes,
// panel: {
// swipe: "left"
// }
panel: {
swipe: "left"
}
});
// Init/Create main view

View File

@ -18,8 +18,9 @@ function configureSmartSelectOfCharacters() {
const optgroup = "<optgroup id='optgroup-house-" + house.id + "' label='" + house.name + "'>";
$$("#select-compare").append(optgroup);
});
characters.forEach(char => {
const option = "<option id='" + char.id + "'>" + char.firstName + "</option>";
const option = "<option id='" + char.id + "' data-option-image='" + "img/characters/" + char.firstName + ".png" + "'>" + char.firstName + "</option>";
$$("#optgroup-house-" + char.idHouse).append(option);
});

View File

@ -6,45 +6,60 @@ Description : Communicates with the server to ensure that the data is up to date
const BASE_URL = "http://localhost/";
let feData = JSON.parse(localStorage.getItem("feData"));
if (feData != null) {
let body = new FormData();
body.append('version', feData.version);
function isOnline() {
// Check that navigator.connection is not undefined to avoid error
// Necessary when the app run on the browser
const networkState = (navigator.connection) ? navigator.connection.type : null;
fetch(BASE_URL + "update", {
method: "POST",
body: body,
})
.then(res => {
if (res.ok) {
return res.json();
} else {
console.log(res);
}
})
.then(data => {
updateData(data);
})
.catch(err => {
console.log(err);
});
} else {
// Import the data if none was found
fetch(BASE_URL + "all")
.then(res => {
if (res.ok) {
return res.json();
} else {
console.log(res);
}
})
.then(data => {
updateData(data);
})
.catch(err => {
console.log(err);
});
if (networkState != null && networkState != "none") {
return true;
}
return false;
}
// Get the data from the server or update the data in the localstorage if needed
function getOrUpdateData() {
if (feData != null) {
let body = new FormData();
body.append('version', feData.version);
fetch(BASE_URL + "update", {
method: "POST",
body: body,
})
.then(res => {
if (res.ok) {
return res.json();
} else {
console.log(res);
}
})
.then(data => {
updateData(data);
})
.catch(err => {
console.log(err);
});
} else {
// Import the data if none was found
fetch(BASE_URL + "all")
.then(res => {
if (res.ok) {
return res.json();
} else {
console.log(res);
}
})
.then(data => {
updateData(data);
})
.catch(err => {
console.log(err);
});
}
}
// Update the data in the localstorage
function updateData(data) {
// If the data received from the server are not empty then we can save them in the local storage
if (data != null && Object.keys(data).length > 0) {
@ -54,4 +69,19 @@ function updateData(data) {
// End of the update process, we can now display the data
displayCharacters();
}
}
// Doesn't try to get or update the data if the user is not connected to the internet
if (isOnline()) {
getOrUpdateData();
} else {
// If the user is not connected to internet but the data are already present in the app, then we display them
if (feData != null) {
displayCharacters();
} else {
alert("Please connect to the internet to download the data");
}
// Create an event to get/update the data when the user goes online
document.addEventListener("online", getOrUpdateData, false);
}

View File

@ -4,17 +4,17 @@ Description : Methods related to the classes of characters
*/
const NB_STATS = 9;
const ID_CLASS_IS_AVAILABLE_FOR_ALL = 1;
function getAvailableClassesForCharacter(char) {
const availableClasses = [];
feData.classes.forEach(feClass => {
if (feClass.isAvailableForAll && (feClass.idGender == ID_GENDER_NON_RESTRICTED || feClass.idGender == char.idGender)) {
if (feClass.isAvailableForAll == ID_CLASS_IS_AVAILABLE_FOR_ALL
&& (feClass.idGender == ID_GENDER_NON_RESTRICTED || feClass.idGender == char.idGender)) {
availableClasses.push(feClass);
} else if (feData.restrictedClasses.find(x => x.idClass == feClass.id && x.idCharacter == char.id)) {
availableClasses.push(feClass);
} else {
if (feData.restrictedClasses.find(x => x.idClass == feClass.id && x.idCharacter == char.id)) {
availableClasses.push(feClass);
}
}
});
return availableClasses;
@ -44,6 +44,7 @@ function getClassGrowthRates(idClass) {
for (let i = 0; i < NB_STATS; i++) {
let gr = partialClassGrowthRates.find(x => (x.idStat == i + 1) ? x : null);
if (gr == null) {
// Create a default growth rate to avoid missing stats
gr = {
idClass: idClass,
idStat: i + 1,

View File

@ -4,6 +4,8 @@ Description : Display the content of the home page
*/
const PRIMARY_NODE_NAME = "Fire Emblem Three House";
// TODO: use color from database
const DEFAUT_COLORS = ["#434348", "#de736d", "#1f3487", "#dfbe4a", "#9be7c0", "#afaad2"];
function displayCharacters() {
const housesWithCharacters = [];
@ -24,7 +26,7 @@ function displayCharacters() {
Highcharts.Series,
'afterSetOptions',
function (e) {
var colors = Highcharts.getOptions().colors,
var colors = DEFAUT_COLORS,
i = 0,
nodes = {};
@ -85,6 +87,7 @@ function displayCharacters() {
data: housesWithCharacters,
events: {
click: function (event) {
// Show the stat page of the character when clicking the node
const charFirstName = event.point.name;
const char = characters.find(x => x.firstName == charFirstName);
if (char != null) {

View File

@ -80,7 +80,13 @@ routes = [
},
{
path: '/about/',
templateUrl: './pages/about.html'
templateUrl: './pages/about.html',
on: {
pageAfterIn: (e, page) => {
if (feData.version)
$$("#catalogue-version").text(feData.version);
},
}
},
];

View File

@ -4,6 +4,7 @@ Description : Display the stats of a character with various graphs
*/
const GRAPH_CONTAINER_CHAR_GR_ID = "container-char-gr";
const SELECTED_CLASS_COLOR = "#f7a35c";
let actualCharId;
function createTableOfStats() {
@ -31,8 +32,8 @@ function displayTableOfGrowthRates(growthRates, callback) {
}
function createListOfAvailableClasses() {
const char = feData.characters.find(x => x.id == actualCharId);
// Get all available classes for the character
const char = feData.characters.find(x => x.id == actualCharId);
const availableClasses = getAvailableClassesForCharacter(char);
// Create the select of available classes
@ -53,6 +54,19 @@ function createListOfAvailableClasses() {
select.on("change", (e) => {
char.idClassSelected = Number($$(e.target).val());
displayGraphOfGrowthRatesForChar();
updateTableOfGrowthRates(char);
});
}
function updateTableOfGrowthRates(char) {
const charGrowthRates = feData.charGrowthRates.map(x => (x.idCharacter == char.id) ? x : null).filter(x => x != null);
classGrowthRates = feData.classGrowthRates.map(x => (x.idClass == char.idClassSelected) ? x : null).filter(x => x != null);
classGrowthRates.forEach(gr => {
const charGr = charGrowthRates.find(x => x.id == gr.id);
const bonus = (gr.value > 0) ? " (+" + gr.value + ")": " (" + gr.value + ")";
$$("#stat-" + gr.idStat).text(charGr.value + bonus);
});
}
@ -65,11 +79,47 @@ function displayGraphOfGrowthRatesForChar() {
}
function displayColumnChartOfCharGrowthRates() {
const char = feData.characters.find(x => x.id == actualCharId);
const statsNames = feData.stats.map(x => x.name);
const charData = [computeCharacterGrowthRatesWithClass(char)]; // The data must be an array
const char = feData.characters.find(x => x.id == actualCharId);
const charGrowthRates = feData.charGrowthRates.map(x => (x.idCharacter == char.id) ? x : null).filter(x => x != null);
displayColumnChart(GRAPH_CONTAINER_CHAR_GR_ID, statsNames, charData);
const charData = {
name: char.firstName,
data: [],
};
const classData = {
data: [],
color: SELECTED_CLASS_COLOR,
};
let classGrowthRates = null;
if (char.idClassSelected != null && char.idClassSelected > 0) {
// Get the selected class of the character
classData.name = feData.classes.find(x => x.id == char.idClassSelected).name;
classGrowthRates = feData.classGrowthRates.map(x => (x.idClass == char.idClassSelected) ? x : null).filter(x => x != null);
}
charGrowthRates.forEach(gr => {
charData.data.push(Number(gr.value));
if (classGrowthRates != null) {
// Compute the growth rates of the character with the growth rates of the class
let classGr = classGrowthRates.find(x => x.idStat == gr.idStat);
if (!classGr) {
classGr = 0;
}
classData.data.push(Number(classGr.value));
}
});
const series = [charData];
if (classGrowthRates != null) {
series.unshift(classData);
}
displayColumnChart(GRAPH_CONTAINER_CHAR_GR_ID, statsNames, series);
}
function displayPolarSpiderOfCharGrowthRates() {

View File

@ -12,9 +12,29 @@
</div>
</div>
<div class="page-content">
<div class="block block-strong">
<p>Aboutpage</p>
<div class="block block-strong text-center">
<section>
<img class="logo" src="img/logo.png">
<h1 class="about">Fire Emblem Charts</h1>
</section>
<section>
<h3 class="about">Versions</h3>
<p>App : 1.0.0</p>
<p>Catalogue : <span id="catalogue-version"></span></p>
</section>
<section>
<h3 class="about">About the app</h3>
<p>
Fire Emblem Charts is a mobile application that allows you to view the statistics of the
characters in the game Fire Emblem Three Houses in the form of graphics.<br>
<a class="link external" href="https://github.com/dario-cfpt/FE_Charts/">Source code</a>
</p>
</section>
<section>
<h3 class="about">Developer</h3>
<p>Dario Genga</p>
</section>
</div>
</div>
</div>
</div>
</div>

View File

@ -18,8 +18,8 @@
</figure>
<div class="block">
<p class="segmented segmented-raised segmented-round">
<button id="btn-graph-spider-web" class="button button-round button-active">Spider web</button>
<button id="btn-graph-column-chart" class="button button-round">Column chart</button>
<button id="btn-graph-spider-web" class="button button-round">Spider web</button>
<button id="btn-graph-column-chart" class="button button-round button-active">Column chart</button>
</p>
</div>
</div>

View File

@ -9,6 +9,9 @@ START TRANSACTION;
INSERT INTO `Tbl_Character_Class` (`Id_Class`, `Id_Character`) VALUES
(7, 3),
(7, 11),
(7, 19),
(32, 1),
(32, 2),
(33, 3),

View File

@ -36,7 +36,7 @@ INSERT INTO `Tbl_Class` (`Id_Class`, `Nm_Class`, `Is_Available_For_All`, `Id_Gen
(25, 'Warrior', 1, 1),
(26, 'Sniper', 1, 1),
(27, 'Grappler', 1, 3),
(28, 'Warlkock', 1, 1),
(28, 'Warlock', 1, 1),
(29, 'Dark Bishop', 1, 3),
(30, 'Bishop', 1, 2),
(31, 'Dancer', 0, 1),

View File

@ -13,7 +13,7 @@ INSERT INTO `Tbl_House` (`Id_House`, `Nm_House`) VALUES
(3, 'Blue Lions'),
(5, 'Church of Seiros'),
(4, 'Golden Deer'),
(1, 'None');
(1, 'Unaffiliated');
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;

File diff suppressed because it is too large Load Diff