Commit e58d4539 authored by Duhoux Pierre-Louis's avatar Duhoux Pierre-Louis

Advance options page + i18n

parent 8d72414f
......@@ -5,8 +5,25 @@ All Mangas Reader is a browser extension which is designed to help you read and
The first version, created in 2012, is no more maintained due to poor code design.
All Mangas Reader V2 has been created to solve the two main issues of the V1 :
- Chrome removed AMR from its store because of terms service violation. Since then, a lot of workarounds have been found to install AMR, but its more difficult to install.
**All Mangas Reader V2 is supported by Firefox, Chrome (Chromium, Canary), Opera, and Edge once it will implement properly native Promises**
**All Mangas Reader V2 is supported by Firefox, Chrome (Chromium, Canary), Opera**, and Edge once it will implement properly native Promises
- The code was really difficult to maintain because of really long and unreadable files full of jQuery code without comments
**All Mangas Reader has been fully rewritten with modern standards (ES6 (Babel), Webpack, VueJS) and is well documented**
## Installation
## Contribute
## Dependencies
- Vue : One of the most popular reactive framework, Vue allows to create great UI
- Vuex : Reactive store for vue, vuex organizes the data model properly
- Vuetify : a set of UI components for view based on Material design
- vuex-shared-mutations : a great plugin for Vuex which allows to synchronize vuex data model through different instances of Vue using localStorage events
- webextension-polyfill : web extension API has been normalize and is implemented in Chrome, Firefox, Edge, ... but with still a few differences, thanks to this polyfill, the code works everywhere
- axios : for xhr
- jQuery : jQuery is still there, to manipulate DOM, it's still the best. Mirrors implementations are based on jQuery and the content script uses it as well. We are stuck in jQuery 2.x.x because a lot of mirrors implementations are still using jQuery functions deprecated in jQuery 3.x (like size())
- regenerator-runtime : the original Facebook plugin to execute ES6 async / await code. When Babel compiles async / await code, it generates references to this plugin. This one is way much lighter than the babel-polyfill which can do the same (and more) and is the reference implementation for babel as well.
## Dev dependencies
All Mangas Reader V2 is based on Webpack to compile the code. It uses Babel to compile EcmaScript 6 syntax, which is much more readable.
\ No newline at end of file
......@@ -8430,11 +8430,6 @@
}
}
},
"vue-router": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.0.1.tgz",
"integrity": "sha512-vLLoY452L+JBpALMP5UHum9+7nzR9PeIBCghU9ZtJ1eWm6ieUI8Zb/DI3MYxH32bxkjzYV1LRjNv4qr8d+uX/w=="
},
"vue-style-loader": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.0.2.tgz",
......
{
"description": {
"message": "Follow updates of online manga readers. Beautify reading, with full chapters scans.",
"description": "Description of the extension."
},
"licence": {
"message": "All Mangas Reader is under the licence of GLPv3",
"description": "This is the licencing info"
},
"options_on_websites": {
"message": "While reading mangas",
"description": "This is the header of the options page tab about reading mangas"
},
"options_general": {
"message": "General",
"description": "This is the header of the options page tab general"
},
"options_supported": {
"message": "Supported websites",
"description": "This is the header of the options page tab about supported websites"
},
"options_web_chapter_display_mode": {
"message": "Chapter display mode",
"description": "This is the title of the options page section chapter display mode"
},
"options_web_chapter_display_chapter_opt": {
"message": "Display chapters",
"description": "Option display Chapter"
},
"options_web_chapter_display_chapter_desc": {
"message": "While reading a manga, display the whole chapter instead of the current page",
"description": "Option display Chapter description"
},
"options_web_chapter_display_mode_opt": {
"message": "Chapters display mode",
"description": "Option Chapter display mode"
},
"options_web_chapter_display_mode_1": {
"message": "Scans are displayed on top of one another",
"description": "Option Chapter display mode 1 --> images are displayed on top of one another"
},
"options_web_chapter_display_mode_2": {
"message": "Scans are displayed two by two occidental reading mode",
"description": "Option Chapter display mode 2 --> images are displayed two by two occidental reading mode"
},
"options_web_chapter_display_mode_3": {
"message": "Scans are displayed two by two japanese reading mode",
"description": "Option Chapter display mode 3 --> images are displayed two by two japanese reading mode"
},
"options_web_resize_opt": {
"message": "Resize scans",
"description": "Option Resize scans"
},
"options_web_resize_desc": {
"message": "Resize scans images to fit screen if they are to wide",
"description": "Option Resize scans description"
},
"options_web_loading": {
"message": "Loading options",
"description": "This is the title of the options page section loading"
},
"options_web_load_opt": {
"message": "Loading progression",
"description": "Option Loading progression"
},
"options_web_load_desc": {
"message": "See loading progression in the title bar",
"description": "Option Loading progression description"
},
"options_web_imgorder_opt": {
"message": "Load scans in order",
"description": "Option Loading sans order"
},
"options_web_imgorder_desc": {
"message": "Load scans in the order of the chapter (This option involve that the chapter's loading will be slower but guarantees that the first scan which will be loaded is the first of the chapter and so on...)",
"description": "Option Loading sans order description"
},
"options_web_prefetch_opt": {
"message": "Prefetch next chapter",
"description": "Option prefetch next chapter"
},
"options_web_prefetch_desc": {
"message": "Prefetch next chapter while reading manga. (A progress bar indicating the prefetch level of the next chapter appears inside the \"Next\" button)",
"description": "Option prefetch next chapter description"
},
"options_web_markwhendownload_opt": {
"message": "Update once fully loaded",
"description": "Option Update once fully loaded"
},
"options_web_markwhendownload_desc": {
"message": "Mark chapter as read when the full chapter has been downloaded",
"description": "Option Update once fully loaded description"
},
"options_web_facilities": {
"message": "Facilities",
"description": "This is the title of the options page section facilities"
},
"options_web_newbar_opt": {
"message": "Navigation in one top bar",
"description": "Option Navigation in one top bar"
},
"options_web_newbar_desc": {
"message": "Display navigation features in one always visible bar. (If you uncheck it, two navigation panels are included in each page, one on top of the scans and one at the bottom)",
"description": "Option Navigation in one top bar description"
},
"options_web_addauto_opt": {
"message": "Automatically add manga to updates list",
"description": "Option automatically add manga to list"
},
"options_web_addauto_desc": {
"message": "Automatically add manga in my reading list when I read it. If you uncheck this option, you will have a button in the navigation bar to add the current manga in the list.",
"description": "Option automatically add manga to list description"
},
"options_web_lrkeys_opt": {
"message": "Use keys to read manga",
"description": "Option Use keys to read manga"
},
"options_web_lrkeys_desc": {
"message": "Use left /right keys while reading to go previous / next scan.",
"description": "Option Use keys to read manga description"
},
"options_web_autobm_opt": {
"message": "Quickly bookmark scans",
"description": "Option Quickly bookmark scans"
},
"options_web_autobm_desc": {
"message": "Automatically Bookmark scans when double click on it.",
"description": "Option Quickly bookmark scans description"
},
"options_web_rightnext_opt": {
"message": "Next chapter when read ended (right key)",
"description": "Option next chapter on right key"
},
"options_web_rightnext_desc": {
"message": "Right key goes to next chapter when at bottom of the page.",
"description": "Option next chapter on right key description"
},
"options_gen_aspect": {
"message": "Customization",
"description": "This is the title of the options page section customization"
},
"options_gen_newTab_opt": {
"message": "Open popup in new tab",
"description": "Option Open popup in new tab"
},
"options_gen_newTab_desc": {
"message": "Open my manga reading list in a new tab when i click on the All Mangas Reader's icon",
"description": "Option Open popup in new tab description"
},
"options_gen_groupmgs_opt": {
"message": "Group mangas by name",
"description": "Option Group mangas by name"
},
"options_gen_groupmgs_desc": {
"message": "Group mangas which have the same name on one line. When mangas are grouped, you can see all occurrences of the manga by clicking on the + icon next to the manga's name.",
"description": "Option Group mangas by name description"
},
"options_gen_displastup_opt": {
"message": "Badge with last time updated on manga",
"description": "Option Badge with last time updated"
},
"options_gen_displastup_desc": {
"message": "Display a badge with a calendar icon representing the last time a new chapter has been published",
"description": "Option Badge with last time updated description"
},
"options_gen_dark_opt": {
"message": "Dark background",
"description": "Option Dark popup"
},
"options_gen_dark_desc": {
"message": "Use a dark background in All Mangas Reader pages",
"description": "Option Dark popup description"
},
"options_gen_colors_new": {
"message": "Color of mangas with unread chapters",
"description": "Option color of mangas new"
},
"options_gen_colors_read": {
"message": "Color of mangas with all chapters read",
"description": "Option color of mangas read"
},
"options_gen_colors_notfollow": {
"message": "Color of mangas which you choose to stop following",
"description": "Option color of mangas not followed"
},
"options_gen_updates": {
"message": "Updates",
"description": "This is the title of the options page section updates"
},
"options_gen_update_chap_label": {
"message": "Update chapters list every",
"description": "Option update chapters list"
},
"options_minutes": {
"message": "$1 minutes",
"description": "Display a number of minutes"
},
"options_hours": {
"message": "$1 hours",
"description": "Display a number of hours"
},
"options_days": {
"message": "$1 days",
"description": "Display a number of days"
},
"options_week": {
"message": "$1 week",
"description": "Display a number of week"
},
"options_update_chap_btn": {
"message": "Refresh now",
"description": "Button refresh chapters list now"
}
}
\ No newline at end of file
import browser from "webextension-polyfill";
/**
* To reduce code size of i18n
* @param {*} message
* @param {*} args
*/
export default function(message, ...args) {
return browser.i18n.getMessage(message, [...args]);
}
\ No newline at end of file
......@@ -15,7 +15,7 @@ import * as utils from '../amr/utils';
*/
utils.debug("Initialize options");
await store.dispatch('initOptions');
/**
* Initialize mirrors list in store from DB
*/
......
{
"name": "All Mangas Reader",
"description": "All Mangas Reader",
"description": "__MSG_description__",
"author": "",
"version": "2.0.0",
"manifest_version": 2,
"default_locale": "en",
"icons": {
"48": "icons/icon_48.png",
"128": "icons/icon_128.png"
......@@ -36,6 +37,7 @@
"tabs",
"https://*/*",
"http://*/*",
"unlimitedStorage",
"contextMenus",
"notifications"
],
......
<template>
<v-app>
<v-app :dark="$store.state.options.dark === 1">
<v-toolbar app>
<img src="/icons/icon_32.png" alt="All Mangas Reader">
<v-toolbar-title v-text="title"></v-toolbar-title>
<v-spacer></v-spacer>
<v-btn icon @click.stop="options = true">
<v-btn icon @click.stop="openOptions()">
<v-icon>mdi-settings</v-icon>
</v-btn>
<v-btn icon @click.stop="bottomSearch = !bottomSearch">
......@@ -32,6 +32,7 @@
</v-toolbar>
<v-content>
<MangaList></MangaList>
<div id="__bottom_app__"></div>
</v-content>
<v-dialog
v-model="options"
......@@ -41,8 +42,8 @@
scrollable
>
<v-card tile>
<v-toolbar card dark color="red darken-1">
<v-btn icon @click.native="options = false" dark>
<v-toolbar card dark color="primary">
<v-btn icon @click.native="closeOptions()" dark>
<v-icon>close</v-icon>
</v-btn>
<v-toolbar-title>Settings</v-toolbar-title>
......@@ -56,31 +57,42 @@
<script>
import MangaList from "./components/MangaList";
import Options from "./components/Options";
import PopupResizer from './resizePopup';
export default {
data() {
return {
bottomSearch: false,
bottomOptions: false,
title: "All Mangas Reader",
options: false
title: "All Mangas Reader",
options: false
};
},
name: "App",
components: { MangaList, Options },
created() {
// initialize state for store in popup from background
this.$store.dispatch("getStateFromReference", {
module: "options",
mutation: "extendOptions"
});
// initialize state for store in popup from background
this.$store.dispatch("getStateFromReference", {
module: "mirrors",
key: "all",
mutation: "setMirrors"
});
}
},
methods: {
openOptions() {
this.options = true;
PopupResizer.setHeightToMax();
},
closeOptions() {
this.options = false;
PopupResizer.setHeightToCurrent();
}
},
mounted: function () {
this.$nextTick(function () {
PopupResizer.checkHeight();
})
}
};
</script>
<style>
......
......@@ -124,11 +124,15 @@ export default {
outline: none;
display: -webkit-inline-box;
display: -ms-inline-flexbox;
font-size:11px;
}
.cat-act {
font-size: 12px;
cursor: pointer;
}
.application .theme--dark.icon, .theme--dark .icon.cat-act {
color: rgba(0,0,0,.54);
}
.cat-act:hover {
opacity: 0.6;
}
......@@ -147,13 +151,17 @@ export default {
border-radius: 2px;
outline: none;
transition: 0.3s cubic-bezier(0.25, 0.8, 0.5, 1);
color: black;
}
.cat-chip.include {
background: #e0e0e0;
.cat-chip:hover {
background: #777;
}
.cat-chip.exclude {
.cat-chip.include, .cat-chip.exclude {
background: #e0e0e0;
}
.cat-chip.include:hover, .cat-chip.exclude:hover {
background: #a0a0a0;
}
.cat-chip.exclude .cat-name {
text-decoration: line-through;
}
......
......@@ -39,7 +39,7 @@
</v-tooltip>
</div>
<!-- Loading bar if chapters list is not loaded yet-->
<v-progress-linear v-if="!manga.listChaps.length" :indeterminate="true" height="4" class="amr-manga-waiting"></v-progress-linear>
<v-progress-linear v-if="!manga.listChaps.length" :indeterminate="true" height="4" class="amr-manga-waiting" :color="color(1)"></v-progress-linear>
</v-card>
</v-flex>
<!-- Actions -->
......@@ -168,14 +168,14 @@ export default {
light === 0
? ""
: light < 0 ? " darken-" + -light : " lighten-" + light;
if (this.manga.read !== 0) return "blue-grey" + lstr;
if (this.manga.read !== 0) return this.options.colornotfollow + lstr;
else if (
this.manga.listChaps.length &&
this.manga.lastChapterReadURL !== this.manga.listChaps[0][1]
) {
return "green" + lstr;
return this.options.colornew + lstr;
} else {
return "blue" + lstr;
return this.options.colorread + lstr;
}
},
/**
......@@ -225,10 +225,7 @@ export default {
}
},
// Name of the component
name: "Manga",
mount: function() {
console.log(this.manga);
}
name: "Manga"
};
</script>
......
......@@ -127,14 +127,14 @@ export default {
light === 0
? ""
: light < 0 ? " darken-" + -light : " lighten-" + light;
if (this.first.read !== 0) return "blue-grey" + lstr;
if (this.first.read !== 0) return this.options.colornotfollow + lstr;
else if (
this.first.listChaps.length &&
this.first.lastChapterReadURL !== this.first.listChaps[0][1]
) {
return "green" + lstr;
return this.options.colornew + lstr;
} else {
return "blue" + lstr;
return this.options.colorread + lstr;
}
},
/**
......@@ -226,6 +226,10 @@ select {
padding: 2px 4px;
padding-right: 15px;
color: white;
font-size: 11px;
}
select option {
font-size: 11px;
}
.det-sel-wrapper:after {
content: "▼";
......
......@@ -135,9 +135,15 @@ export default {
margin-left: 4px;
cursor: pointer;
}
.theme--dark .icon.amr-filter {
color:grey;
}
.amr-filter.activated {
color: black;
}
.theme--dark .icon.amr-filter.activated {
color: white;
}
.flip-list-move {
transition: transform 1s;
}
......
This diff is collapsed.
......@@ -4,6 +4,7 @@ html, body{
min-width:700px;
/*min-height: 200px;*/
max-height: 500px;
overflow: hidden;
}
html {
overflow-y: hidden;
......@@ -12,4 +13,6 @@ html {
.icon-ttip {
margin-top: -15px;
}
body {
font-size:11px;
}
......@@ -7,13 +7,23 @@ import Vue from 'vue';
import Vuetify from 'vuetify';
import App from './App.vue';
import store from './../store';
import theme from './theme';
Vue.config.productionTip = false
Vue.use(Vuetify)
new Vue({
el: '#app',
store,
render: h => h(App)
});
(async function() {
// Load options in store before everything
await store.dispatch("getStateFromReference", {
module: "options",
mutation: "extendOptions"
});
// Load vue
Vue.config.productionTip = false
Vue.use(Vuetify, {theme: theme})
new Vue({
el: '#app',
store,
render: h => h(App)
});
})();
\ No newline at end of file
/**
* This class allows AMR to resize correctly the popup when DOM is changed
*/
class PopupResizer {
constructor() {
this.maxHeight = 500;
this.oldHeight = 0;
this.bottomel;
this.starttime = 0;
this.boundListener;
this.clicked = false;
this.workingTime = 3000;
this.stop = false;
}
/**
* This method is using requestAnimationFrame to watch for the bottom of the app to move. It resizes
* the popup consequently. It runs for two seconds and then stops after registering a click listener.
* On click, it is restarted for workingTime milliseconds. Perhaps we will need to add other events
* listeners which action could affect the popup's height
*/
checkHeight() {
if (!this.bottomel) this.bottomel = document.getElementById("__bottom_app__");
if (this.stop || this.starttime > 0 && Date.now() - this.starttime > this.workingTime) {
this.boundListener = this.clickWindow.bind(this);
window.addEventListener("click", this.boundListener);
//console.log("stop checking");
this.starttime = 0;
this.clicked = false;
this.stop = false;
return;
}
if (this.bottomel) {
if (this.starttime == 0) this.starttime = Date.now();
let cur = this.bottomel.getBoundingClientRect().bottom;
if (cur !== this.oldHeight) {
this.oldHeight = cur;
document.getElementsByTagName("body")[0].style["height"] = cur + "px";
document.documentElement.style["height"] = cur + "px";
}
}
requestAnimationFrame(this.checkHeight.bind(this));
}
/**
* We restart the height watcher when a click in the popup occurs
*/
clickWindow() {
if (this.clicked) return; // just to be sure we can't enter this function two times
this.clicked = true;
//console.log("check again");
window.removeEventListener("click", this.boundListener);
this.checkHeight();
}
/**
* Sets the current height of the popup to the height of content
*/
setHeightToCurrent() {
//console.log("set height to current");
if (!this.bottomel) this.bottomel = document.getElementById("__bottom_app__");
let cur = this.bottomel.getBoundingClientRect().bottom
document.getElementsByTagName("body")[0].style["height"] = cur + "px";
document.documentElement.style["height"] = cur + "px";
this.clickWindow();
}
/**
* Set height to max (when options is opened for example)
*/
setHeightToMax() {
//console.log("set max height");
this.stop = true;
document.getElementsByTagName("body")[0].style["height"] = this.maxHeight + "px";
document.documentElement.style["height"] = this.maxHeight + "px";
}
}
export default (new PopupResizer)
\ No newline at end of file
import colors from 'vuetify/es5/util/colors'
/**
* Vuetify theme used by All Mangas Reader
*/
export default {
primary: colors.red.base,
secondary: colors.grey.darken1,
accent: colors.red.lighten1,
error: colors.red.accent3
}
\ No newline at end of file
......@@ -35,7 +35,7 @@ export default new Vuex.Store({
*/
async getStateFromReference({ commit }, { module, key, mutation }) {
return new Promise(async (resolve, reject) => {
browser.runtime.sendMessage({ action: "vuex_initstate", module: module, key: key })
await browser.runtime.sendMessage({ action: "vuex_initstate", module: module, key: key })
.then(async object => {
await commit(mutation, object)
resolve()
......
......@@ -6,6 +6,7 @@ import Axios from 'axios';
* Each option MUST figure in this object
*/
const default_options = {
debug: 1, // display debug traces in content script, background, popup, ...
/**
* Options used by background script
*/
......@@ -29,14 +30,27 @@ const default_options = {
displayMode: 3,
addauto: 1, // automatically mark chapters as read while reading
resize: 1, // resize scans to fit in viewport
debug: 1, // display debug traces in content script, background, popup, ...
autobm: 1, // bookmark automatically the scans when dlbclicked in page
markwhendownload: 0, // mark mangas as read when all images downloaded
prefetch: 1, // load next chapter in background while reading
groupmgs: 1, // group manga with similar name (one piece and One Piece)
lrkeys: 1, // use arrows keys to read chapter
rightnext: 1,
//TO IMPLEMENT
load: 0, //See loading progression in the title bar
imgorder: 0, //Load scans in order
/** Customization options */
newTab: 0, //Open popup in new tab
displastup: 0, // Display a badge with last time updated in popup
dark: 0, //DONE // Use a dark backgroup for AMR pages,
colornew: "green", //DONE // color of mangas with new chapters
colorread: "blue", //DONE// color of mangas with all chapters read
colornotfollow: "blue-grey", //DONE // color of mangas which are not followed
/** Updates options */
updatechap: 1800000, // update chapters frequency
/**
* Categories states, each custom category is stored in localStorage in this array
* states are
......@@ -53,6 +67,7 @@ const default_options = {
}
const jsonOptions = ["categoriesStates", "impl-repositories"];
const stringOptions = ["colornew", "colorread", "colornotfollow"];
/**
* initial state of amr options
......@@ -78,6 +93,11 @@ const actions = {
let storedVal = localStorage["o." + key];
if (storedVal) {
if (jsonOptions.includes(key)) storedVal = JSON.parse(storedVal);
else {
if (!stringOptions.includes(key)) {
storedVal = parseInt(storedVal); // all non Json and non String values are considered Integers --> this is right for now
}
}
commit('setOption', { key: key, value: storedVal });
}
}
......