Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4eb225cdf3 | |||
| 3cb41c8bfb | |||
| 05511e073e | |||
| 085b9b9eb3 | |||
| 9290d59f77 | |||
| c0345ae9a9 | |||
| c12a7f378a | |||
| d3c2d130e1 | |||
| 134c72a683 | |||
| 4c326e35c0 | |||
| 419420efd1 | |||
| 871fc905ce | |||
| 5a8c652f68 | |||
| 97f2f84fa5 | |||
| 65a3e8fd2d |
@@ -6,13 +6,11 @@ import AppLoading from "expo-app-loading";
|
||||
|
||||
import Router from "./src/navigation/Router";
|
||||
|
||||
import { createTables, createIndexes } from "./src/services/db";
|
||||
import { setStatusBar } from "./src/services/asyncStorage";
|
||||
|
||||
// Redux
|
||||
import { Provider } from "react-redux";
|
||||
import { persistor, store } from "./src/redux/store/configureStore";
|
||||
import { persistor, store } from "./src/redux/store";
|
||||
import { PersistGate } from "redux-persist/lib/integration/react";
|
||||
import { createDB } from "./src/database/DBHelper";
|
||||
|
||||
const getFonts = () =>
|
||||
Font.loadAsync({
|
||||
@@ -22,15 +20,8 @@ const getFonts = () =>
|
||||
const App = () => {
|
||||
const [fontsLoaded, setFontsLoaded] = useState(false);
|
||||
|
||||
setStatusBar();
|
||||
|
||||
const createDb = () => {
|
||||
createTables();
|
||||
createIndexes();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
createDb();
|
||||
createDB();
|
||||
}, []);
|
||||
|
||||
if (fontsLoaded) {
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 8.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 24 KiB |
Generated
+408
-3
@@ -2140,6 +2140,11 @@
|
||||
"resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.1.0.tgz",
|
||||
"integrity": "sha512-afmTuJrylUU/0OtqzaRkbyYFFNgCF73Bvel/sw90pvGrWIZ+vyoIJqA6eMSoA6+nb443kTmulmBtC9NerXboNg=="
|
||||
},
|
||||
"@sqltools/formatter": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.2.tgz",
|
||||
"integrity": "sha512-/5O7Fq6Vnv8L6ucmPjaWbVG1XkP4FO+w5glqfkIsq3Xw4oyNAdJddbnYodNDAfjVUvo/rrSCTom4kAND7T1o5Q=="
|
||||
},
|
||||
"@types/hammerjs": {
|
||||
"version": "2.0.36",
|
||||
"resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.36.tgz",
|
||||
@@ -2172,9 +2177,9 @@
|
||||
"integrity": "sha512-A3YT/c1oTlyvvW/GQqG86EyqWNrT/tisOIh2mW3YCgcx71TNjiTZA3zYZWA5BCmtsOTXjhliy4c4yEkErw6njA=="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "14.14.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.14.tgz",
|
||||
"integrity": "sha512-UHnOPWVWV1z+VV8k6L1HhG7UbGBgIdghqF3l9Ny9ApPghbjICXkUJSd/b9gOgQfjM1r+37cipdw/HJ3F6ICEnQ=="
|
||||
"version": "14.14.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.35.tgz",
|
||||
"integrity": "sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag=="
|
||||
},
|
||||
"@types/stack-utils": {
|
||||
"version": "1.0.1",
|
||||
@@ -2308,6 +2313,11 @@
|
||||
"resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz",
|
||||
"integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg=="
|
||||
},
|
||||
"any-promise": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
|
||||
"integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
|
||||
},
|
||||
"anymatch": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
|
||||
@@ -2317,6 +2327,11 @@
|
||||
"normalize-path": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"app-root-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.0.0.tgz",
|
||||
"integrity": "sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw=="
|
||||
},
|
||||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
@@ -2826,6 +2841,116 @@
|
||||
"restore-cursor": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"cli-highlight": {
|
||||
"version": "2.1.10",
|
||||
"resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.10.tgz",
|
||||
"integrity": "sha512-CcPFD3JwdQ2oSzy+AMG6j3LRTkNjM82kzcSKzoVw6cLanDCJNlsLjeqVTOTfOfucnWv5F0rmBemVf1m9JiIasw==",
|
||||
"requires": {
|
||||
"chalk": "^4.0.0",
|
||||
"highlight.js": "^10.0.0",
|
||||
"mz": "^2.4.0",
|
||||
"parse5": "^5.1.1",
|
||||
"parse5-htmlparser2-tree-adapter": "^6.0.0",
|
||||
"yargs": "^16.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"cliui": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
|
||||
"requires": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"requires": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz",
|
||||
"integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg=="
|
||||
},
|
||||
"yargs": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
||||
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
|
||||
"requires": {
|
||||
"cliui": "^7.0.2",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.0",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^20.2.2"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "20.2.7",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz",
|
||||
"integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli-spinners": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.5.0.tgz",
|
||||
@@ -3241,6 +3366,11 @@
|
||||
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
|
||||
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
|
||||
},
|
||||
"dotenv": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
|
||||
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
|
||||
},
|
||||
"ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
@@ -3849,6 +3979,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"figlet": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/figlet/-/figlet-1.5.0.tgz",
|
||||
"integrity": "sha512-ZQJM4aifMpz6H19AW1VqvZ7l4pOE9p7i/3LyxgO2kp+PO/VcDYNqIHEMtkccqIhTXMKci4kjueJr/iCQEaT/Ww=="
|
||||
},
|
||||
"figures": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
|
||||
@@ -4091,6 +4226,21 @@
|
||||
"function-bind": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"has-ansi": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
|
||||
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||
}
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
@@ -4150,6 +4300,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"highlight.js": {
|
||||
"version": "10.7.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.1.tgz",
|
||||
"integrity": "sha512-S6G97tHGqJ/U8DsXcEdnACbirtbx58Bx9CzIVeYli8OuswCfYI/LsXH2EiGcoGio1KAC3x4mmUwulOllJ2ZyRA=="
|
||||
},
|
||||
"hoist-non-react-statics": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||
@@ -6006,6 +6161,16 @@
|
||||
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
|
||||
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
|
||||
},
|
||||
"mz": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
||||
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
|
||||
"requires": {
|
||||
"any-promise": "^1.0.0",
|
||||
"object-assign": "^4.0.1",
|
||||
"thenify-all": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.14.2",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
|
||||
@@ -6338,6 +6503,11 @@
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
|
||||
},
|
||||
"parent-require": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz",
|
||||
"integrity": "sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc="
|
||||
},
|
||||
"parse-bmfont-ascii": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz",
|
||||
@@ -6376,6 +6546,26 @@
|
||||
"resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
|
||||
"integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA=="
|
||||
},
|
||||
"parse5": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
|
||||
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug=="
|
||||
},
|
||||
"parse5-htmlparser2-tree-adapter": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
|
||||
"integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
|
||||
"requires": {
|
||||
"parse5": "^6.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"parse5": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
|
||||
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
@@ -7120,6 +7310,14 @@
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"react-native-input-spinner": {
|
||||
"version": "1.7.7",
|
||||
"resolved": "https://registry.npmjs.org/react-native-input-spinner/-/react-native-input-spinner-1.7.7.tgz",
|
||||
"integrity": "sha512-6zYFU2uhChJ+JC/5yujlZu+r5PYQSYIXluUqTNR3jOCKai0dlCkWiz7d3K2rM7Q2SAIiw4bSBR6mWZIuhL+DEg==",
|
||||
"requires": {
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"react-native-iphone-x-helper": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz",
|
||||
@@ -7293,6 +7491,11 @@
|
||||
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
|
||||
"integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw=="
|
||||
},
|
||||
"reflect-metadata": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
|
||||
"integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg=="
|
||||
},
|
||||
"regenerate": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
||||
@@ -7638,6 +7841,15 @@
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
|
||||
},
|
||||
"sha.js": {
|
||||
"version": "2.4.11",
|
||||
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
|
||||
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"shebang-command": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
|
||||
@@ -8083,6 +8295,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"thenify": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
||||
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
|
||||
"requires": {
|
||||
"any-promise": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"thenify-all": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
|
||||
"integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=",
|
||||
"requires": {
|
||||
"thenify": ">= 3.1.0 < 4"
|
||||
}
|
||||
},
|
||||
"throat": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz",
|
||||
@@ -8183,6 +8411,11 @@
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz",
|
||||
@@ -8193,6 +8426,131 @@
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
||||
},
|
||||
"typeorm": {
|
||||
"version": "0.2.31",
|
||||
"resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.2.31.tgz",
|
||||
"integrity": "sha512-dVvCEVHH48DG0QPXAKfo0l6ecQrl3A8ucGP4Yw4myz4YEDMProebTQo8as83uyES+nrwCbu3qdkL4ncC2+qcMA==",
|
||||
"requires": {
|
||||
"@sqltools/formatter": "1.2.2",
|
||||
"app-root-path": "^3.0.0",
|
||||
"buffer": "^5.5.0",
|
||||
"chalk": "^4.1.0",
|
||||
"cli-highlight": "^2.1.10",
|
||||
"debug": "^4.1.1",
|
||||
"dotenv": "^8.2.0",
|
||||
"glob": "^7.1.6",
|
||||
"js-yaml": "^3.14.0",
|
||||
"mkdirp": "^1.0.4",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"sha.js": "^2.4.11",
|
||||
"tslib": "^1.13.0",
|
||||
"xml2js": "^0.4.23",
|
||||
"yargonaut": "^1.1.2",
|
||||
"yargs": "^16.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"cliui": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
|
||||
"requires": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"requires": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz",
|
||||
"integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg=="
|
||||
},
|
||||
"yargs": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
||||
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
|
||||
"requires": {
|
||||
"cliui": "^7.0.2",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.0",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^20.2.2"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "20.2.7",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz",
|
||||
"integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"typescript-compare": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz",
|
||||
@@ -8699,6 +9057,53 @@
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
},
|
||||
"yargonaut": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/yargonaut/-/yargonaut-1.1.4.tgz",
|
||||
"integrity": "sha512-rHgFmbgXAAzl+1nngqOcwEljqHGG9uUZoPjsdZEs1w5JW9RXYzrSvH/u70C1JE5qFi0qjsdhnUX/dJRpWqitSA==",
|
||||
"requires": {
|
||||
"chalk": "^1.1.1",
|
||||
"figlet": "^1.1.1",
|
||||
"parent-require": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
|
||||
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"requires": {
|
||||
"ansi-styles": "^2.2.1",
|
||||
"escape-string-regexp": "^1.0.2",
|
||||
"has-ansi": "^2.0.0",
|
||||
"strip-ansi": "^3.0.0",
|
||||
"supports-color": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
|
||||
}
|
||||
}
|
||||
},
|
||||
"yargs": {
|
||||
"version": "15.4.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
||||
|
||||
+6
-2
@@ -30,6 +30,7 @@
|
||||
"react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz",
|
||||
"react-native-collapsible-header-views": "^1.1.2",
|
||||
"react-native-gesture-handler": "^1.8.0",
|
||||
"react-native-input-spinner": "^1.7.7",
|
||||
"react-native-offline": "^5.7.0",
|
||||
"react-native-paper": "^4.5.0",
|
||||
"react-native-reanimated": "^1.13.2",
|
||||
@@ -43,10 +44,13 @@
|
||||
"redux-devtools-extension": "^2.13.9",
|
||||
"redux-persist": "^6.0.0",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"rn-sliding-up-panel": "^2.4.4"
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rn-sliding-up-panel": "^2.4.4",
|
||||
"typeorm": "^0.2.31"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "~7.9.0"
|
||||
"@babel/core": "~7.9.0",
|
||||
"@types/node": "^14.14.35"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
|
||||
@@ -59,17 +59,22 @@ export const SearchAppbar = ({
|
||||
}}
|
||||
>
|
||||
<View style={{ flex: 1, flexDirection: "row" }}>
|
||||
<IconButton
|
||||
icon={screen === "Library" ? "magnify" : "arrow-left"}
|
||||
color={theme.textColorSecondary}
|
||||
style={{ marginLeft: 0 }}
|
||||
size={23}
|
||||
onPress={() => {
|
||||
if (screen === "Extension") {
|
||||
navigation.goBack();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{screen === "Library" ? (
|
||||
<IconButton
|
||||
icon="magnify"
|
||||
color={theme.textColorSecondary}
|
||||
style={{ marginLeft: 0 }}
|
||||
size={23}
|
||||
/>
|
||||
) : (
|
||||
<IconButton
|
||||
icon="arrow-left"
|
||||
color={theme.textColorSecondary}
|
||||
style={{ marginLeft: 0 }}
|
||||
size={23}
|
||||
onPress={() => navigation.goBack()}
|
||||
/>
|
||||
)}
|
||||
<TextInput
|
||||
ref={searchRef}
|
||||
style={{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { StyleSheet, View, Text, ImageBackground } from "react-native";
|
||||
import { StyleSheet, View, Text, ImageBackground, Image } from "react-native";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
import { TouchableRipple } from "react-native-paper";
|
||||
|
||||
@@ -12,12 +12,25 @@ const NovelCover = ({ item, onPress, libraryStatus }) => {
|
||||
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
|
||||
// const itemsPerRow = useSelector(
|
||||
// (state) => state.settingsReducer.itemsPerRow
|
||||
// );
|
||||
const itemsPerRow = useSelector(
|
||||
(state) => state.settingsReducer.itemsPerRow
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1 / 3 }}>
|
||||
const height = {
|
||||
1: 550,
|
||||
2: 280,
|
||||
3: 180,
|
||||
4: 140,
|
||||
5: 100,
|
||||
6: 80,
|
||||
7: 70,
|
||||
8: 58,
|
||||
9: 55,
|
||||
10: 50,
|
||||
};
|
||||
|
||||
return displayMode !== 2 ? (
|
||||
<View style={{ flex: 1 / itemsPerRow }}>
|
||||
<TouchableRipple
|
||||
borderless
|
||||
centered
|
||||
@@ -29,13 +42,61 @@ const NovelCover = ({ item, onPress, libraryStatus }) => {
|
||||
{item.novelCover && (
|
||||
<ImageBackground
|
||||
source={{ uri: item.novelCover }}
|
||||
style={styles.logo}
|
||||
style={{ height: height[itemsPerRow] }}
|
||||
imageStyle={[
|
||||
{ borderRadius: 4 },
|
||||
libraryStatus && { opacity: 0.5 },
|
||||
]}
|
||||
progressiveRenderingEnabled={true}
|
||||
>
|
||||
<View style={{ flexDirection: "row", margin: 4 }}>
|
||||
{item.chaptersDownloaded && (
|
||||
<Text
|
||||
style={[
|
||||
{
|
||||
flex: 0,
|
||||
backgroundColor: "#47a84a",
|
||||
color: "#FFFFFF",
|
||||
borderTopLeftRadius: 4,
|
||||
borderBottomLeftRadius: 4,
|
||||
paddingTop: 2,
|
||||
paddingHorizontal: 4,
|
||||
alignSelf: "flex-start",
|
||||
fontSize: 12,
|
||||
},
|
||||
!item.chaptersUnread && {
|
||||
borderTopRightRadius: 4,
|
||||
borderBottomRightRadius: 4,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{item.chaptersDownloaded}
|
||||
</Text>
|
||||
)}
|
||||
{item.chaptersUnread && (
|
||||
<Text
|
||||
style={[
|
||||
{
|
||||
flex: 0,
|
||||
backgroundColor: "#2979FF",
|
||||
color: "#FFFFFF",
|
||||
borderTopRightRadius: 4,
|
||||
borderBottomRightRadius: 4,
|
||||
paddingTop: 2,
|
||||
paddingHorizontal: 4,
|
||||
alignSelf: "flex-start",
|
||||
fontSize: 12,
|
||||
},
|
||||
!item.chaptersDownloaded && {
|
||||
borderTopLeftRadius: 4,
|
||||
borderBottomLeftRadius: 4,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{item.chaptersUnread}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
{displayMode === 0 && (
|
||||
<View style={styles.titleContainer}>
|
||||
<LinearGradient
|
||||
@@ -79,15 +140,42 @@ const NovelCover = ({ item, onPress, libraryStatus }) => {
|
||||
</>
|
||||
</TouchableRipple>
|
||||
</View>
|
||||
) : (
|
||||
<TouchableRipple
|
||||
borderless
|
||||
centered
|
||||
rippleColor={theme.rippleColor}
|
||||
style={styles.listView}
|
||||
onPress={onPress}
|
||||
>
|
||||
<>
|
||||
<Image
|
||||
source={{ uri: item.novelCover }}
|
||||
style={styles.extensionIcon}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
flex: 1,
|
||||
color: theme.textColorPrimary,
|
||||
marginLeft: 16,
|
||||
fontSize: 15,
|
||||
paddingRight: 8,
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{item.novelName}
|
||||
</Text>
|
||||
</>
|
||||
</TouchableRipple>
|
||||
);
|
||||
};
|
||||
|
||||
export default NovelCover;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
logo: {
|
||||
height: 180,
|
||||
},
|
||||
// logo: {
|
||||
// height: 180,
|
||||
// },
|
||||
titleContainer: {
|
||||
flex: 1,
|
||||
justifyContent: "flex-end",
|
||||
@@ -108,4 +196,17 @@ const styles = StyleSheet.create({
|
||||
paddingVertical: 4,
|
||||
borderRadius: 4,
|
||||
},
|
||||
extensionIcon: {
|
||||
width: 42,
|
||||
height: 42,
|
||||
borderRadius: 4,
|
||||
},
|
||||
listView: {
|
||||
flex: 1,
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 12,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
borderRadius: 4,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -6,8 +6,9 @@ export const DisplayCheckbox = ({ displayMode, onPress, value }) => {
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
|
||||
const label = {
|
||||
0: "Compact",
|
||||
1: "Comfortable",
|
||||
0: "Compact Grid",
|
||||
1: "Comfortable Grid",
|
||||
2: "List",
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import * as SQLite from "expo-sqlite";
|
||||
import {
|
||||
createNovelTableQuery,
|
||||
createUrlIndexQuery,
|
||||
createLibraryIndexQuery,
|
||||
} from "./tables/NovelTable";
|
||||
import {
|
||||
createChapterTableQuery,
|
||||
createNovelIdIndexQuery,
|
||||
createUnreadChaptersIndexQuery,
|
||||
} from "./tables/ChapterTable";
|
||||
import {
|
||||
createHistoryTableQuery,
|
||||
createChapterIdIndexQuery,
|
||||
} from "./tables/HistoryTable";
|
||||
import { createDownloadTableQuery } from "./tables/DownloadTable";
|
||||
|
||||
/**
|
||||
* Database Version = 2
|
||||
*/
|
||||
|
||||
const dbName = "lnreader.db";
|
||||
|
||||
const db = SQLite.openDatabase(dbName);
|
||||
|
||||
export const createDB = () => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(createNovelTableQuery);
|
||||
tx.executeSql(createChapterTableQuery);
|
||||
tx.executeSql(createHistoryTableQuery);
|
||||
tx.executeSql(createDownloadTableQuery);
|
||||
|
||||
//Db indexes
|
||||
tx.executeSql(createUrlIndexQuery);
|
||||
tx.executeSql(createLibraryIndexQuery);
|
||||
tx.executeSql(createNovelIdIndexQuery);
|
||||
tx.executeSql(createUnreadChaptersIndexQuery);
|
||||
tx.executeSql(createChapterIdIndexQuery);
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteDb = () => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql("DROP TABLE novels");
|
||||
tx.executeSql("DROP TABLE chapters");
|
||||
tx.executeSql("DROP TABLE history");
|
||||
tx.executeSql("DROP TABLE downloads");
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,160 @@
|
||||
import * as SQLite from "expo-sqlite";
|
||||
const db = SQLite.openDatabase("lnreader.db");
|
||||
|
||||
const insertChaptersQuery = `INSERT INTO chapters (chapterUrl, chapterName, releaseDate, novelId) values (?, ?, ?, ?)`;
|
||||
|
||||
export const insertChapters = async (novelId, chapters) => {
|
||||
db.transaction((tx) => {
|
||||
chapters.map((chapter) =>
|
||||
tx.executeSql(insertChaptersQuery, [
|
||||
chapter.chapterUrl,
|
||||
chapter.chapterName,
|
||||
chapter.releaseDate,
|
||||
novelId,
|
||||
])
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const getChaptersQuery = `SELECT * FROM chapters WHERE novelId = ? ORDER BY chapterId ASC`;
|
||||
|
||||
export const getChapters = (novelId) => {
|
||||
return new Promise((resolve, reject) =>
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
getChaptersQuery,
|
||||
[novelId],
|
||||
(txObj, { rows: { _array } }) => {
|
||||
resolve(_array);
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const getChapterQuery = `SELECT * FROM downloads WHERE downloadChapterId = ?`;
|
||||
|
||||
export const getChapter = async (chapterId) => {
|
||||
return new Promise((resolve, reject) =>
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
getChapterQuery,
|
||||
[chapterId],
|
||||
(tx, results) => {
|
||||
resolve(results.rows.item(0));
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const markChapterReadQuery = `UPDATE chapters SET \`read\` = 1 WHERE chapterId = ?`;
|
||||
|
||||
export const markChapterRead = async (chapterId) => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
markChapterReadQuery,
|
||||
[chapterId],
|
||||
(tx, res) => {},
|
||||
(tx, error) => console.log(error)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const isChapterDownloadedQuery = `SELECT * FROM downloads WHERE downloadChapterId=?`;
|
||||
|
||||
export const isChapterDownloaded = async (chapterId) => {
|
||||
return new Promise((resolve, reject) =>
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
isChapterDownloadedQuery,
|
||||
[chapterId],
|
||||
(txObj, res) => {
|
||||
if (res.rows.length !== 0) {
|
||||
resolve(true);
|
||||
} else {
|
||||
resolve(false);
|
||||
}
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const downloadChapterQuery = `INSERT INTO downloads (downloadChapterId, chapterName, chapterText, prevChapter, nextChapter) VALUES (?, ?, ?, ?, ?)`;
|
||||
|
||||
export const downloadChapter = async (
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapterUrl,
|
||||
chapterId
|
||||
) => {
|
||||
const downloadUrl = `https://lnreader-extensions.herokuapp.com/api/${extensionId}/novel/${novelUrl}${chapterUrl}`;
|
||||
|
||||
const response = await fetch(downloadUrl);
|
||||
const chapter = await response.json();
|
||||
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
`UPDATE chapters SET downloaded = 1 WHERE chapterId = ?`,
|
||||
[chapterId]
|
||||
);
|
||||
tx.executeSql(
|
||||
downloadChapterQuery,
|
||||
[
|
||||
chapterId,
|
||||
chapter.chapterName,
|
||||
chapter.chapterText,
|
||||
chapter.prevChapter,
|
||||
chapter.nextChapter,
|
||||
],
|
||||
(tx, res) =>
|
||||
console.log(`Downloaded Chapter ${chapter.chapterUrl}`),
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteChapter = async (chapterId) => {
|
||||
const updateIsDownloadedQuery = `UPDATE chapters SET downloaded = 0 WHERE chapterId=?`;
|
||||
const deleteChapterQuery = `DELETE FROM downloads WHERE downloadChapterId=?`;
|
||||
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
updateIsDownloadedQuery,
|
||||
[chapterId],
|
||||
(tx, res) => console.log(`Chaptermarked not downapdeod deleted`),
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
tx.executeSql(
|
||||
deleteChapterQuery,
|
||||
[chapterId],
|
||||
(tx, res) => console.log(`Chapter deleted`),
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const getLastReadChapterQuery = `
|
||||
SELECT chapters.*
|
||||
FROM history
|
||||
JOIN chapters
|
||||
ON history.historyChapterId = chapters.chapterId
|
||||
WHERE history.historyNovelId = ?
|
||||
`;
|
||||
|
||||
export const getLastReadChapter = async (novelId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
getLastReadChapterQuery,
|
||||
[novelId],
|
||||
(tx, { rows }) => resolve(rows.item(0)),
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
import * as SQLite from "expo-sqlite";
|
||||
const db = SQLite.openDatabase("lnreader.db");
|
||||
|
||||
const getHistoryQuery = `
|
||||
SELECT history.*, chapters.*, novels.*
|
||||
FROM history
|
||||
JOIN chapters
|
||||
ON history.historyChapterId = chapters.chapterId
|
||||
JOIN novels
|
||||
ON history.historyNovelId = novels.novelId
|
||||
ORDER BY history.historyTimeRead DESC`;
|
||||
|
||||
export const getHistory = async () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
getHistoryQuery,
|
||||
null,
|
||||
(txObj, { rows: { _array } }) => {
|
||||
resolve(_array);
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// const insertHistoryQuery =
|
||||
|
||||
export const insertHistory = async (novelId, chapterId) => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
"INSERT OR REPLACE INTO history (historyNovelId, historyChapterId) VALUES (?, ?)",
|
||||
[novelId, chapterId],
|
||||
(txObj, res) => {},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
tx.executeSql("UPDATE novels SET unread = 0 WHERE novelId = ?", [
|
||||
novelId,
|
||||
]);
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteHistory = async (novelId) => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql("DELETE FROM history WHERE historyNovelId = ?", [
|
||||
novelId,
|
||||
]);
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteAllHistory = () => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql("DELETE FROM history; VACCUM;");
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
import * as SQLite from "expo-sqlite";
|
||||
const db = SQLite.openDatabase("lnreader.db");
|
||||
|
||||
const getLibraryQuery = `
|
||||
SELECT novels.*, C.chaptersUnread, D.chaptersDownloaded
|
||||
FROM novels
|
||||
LEFT JOIN (
|
||||
SELECT chapters.novelId, COUNT(*) AS chaptersUnread
|
||||
FROM chapters
|
||||
WHERE chapters.read = 0
|
||||
GROUP BY chapters.novelId
|
||||
) AS C
|
||||
ON novels.novelId = C.novelId
|
||||
LEFT JOIN (
|
||||
SELECT chapters.novelId, COUNT(*) AS chaptersDownloaded
|
||||
FROM chapters
|
||||
WHERE chapters.downloaded = 1
|
||||
GROUP BY chapters.novelId
|
||||
) AS D
|
||||
ON novels.novelId = D.novelId
|
||||
WHERE novels.followed = 1
|
||||
GROUP BY novels.novelId
|
||||
`;
|
||||
|
||||
export const getLibrary = () => {
|
||||
return new Promise((resolve, reject) =>
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
getLibraryQuery,
|
||||
null,
|
||||
(txObj, { rows: { _array } }) => {
|
||||
// console.log(_array);
|
||||
resolve(_array);
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
// const searchLibraryQuery = ``;
|
||||
|
||||
export const searchLibrary = (searchText) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
`SELECT * FROM novels WHERE followed = 1 AND novelName LIKE '%${searchText}%'`,
|
||||
null,
|
||||
(txObj, { rows: { _array } }) => resolve(_array),
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,87 @@
|
||||
import * as SQLite from "expo-sqlite";
|
||||
const db = SQLite.openDatabase("lnreader.db");
|
||||
|
||||
const insertNovelQuery = `INSERT INTO novels (novelUrl, sourceUrl, sourceId, source, novelName, novelCover, novelSummary, author, artist, status, genre) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
||||
|
||||
export const insertNovel = async (novel) => {
|
||||
return new Promise((resolve, reject) =>
|
||||
db.transaction((tx) =>
|
||||
tx.executeSql(
|
||||
insertNovelQuery,
|
||||
[
|
||||
novel.novelUrl,
|
||||
novel.sourceUrl,
|
||||
novel.sourceId,
|
||||
novel.source,
|
||||
novel.novelName,
|
||||
novel.novelCover,
|
||||
novel.novelSummary,
|
||||
novel.author,
|
||||
novel.artist,
|
||||
novel.status,
|
||||
novel.genre,
|
||||
],
|
||||
(txObj, { insertId }) => resolve(insertId),
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export const followNovel = async (followed, novelId) => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
"UPDATE novels SET followed = ? WHERE novelId = ?",
|
||||
[!followed, novelId],
|
||||
(tx, res) => {},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const checkNovelInCacheQuery = `SELECT * FROM novels WHERE novelUrl=? LIMIT 1`;
|
||||
|
||||
export const checkNovelInCache = (novelUrl) => {
|
||||
return new Promise((resolve, reject) =>
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
checkNovelInCacheQuery,
|
||||
[novelUrl],
|
||||
(txObj, res) => {
|
||||
if (res.rows.length !== 0) {
|
||||
resolve(true);
|
||||
} else {
|
||||
resolve(false);
|
||||
}
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const getNovel = async (novelUrl) => {
|
||||
return new Promise((resolve, reject) =>
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
`SELECT * FROM novels WHERE novelUrl = ?`,
|
||||
[novelUrl],
|
||||
(txObj, { rows }) => resolve(rows.item(0)),
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const deleteNovelCache = () => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
"DELETE FROM novels WHERE followed = 0",
|
||||
null,
|
||||
(txObj, res) => {
|
||||
console.log("Deleted");
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
export const createChapterTableQuery = `
|
||||
CREATE TABLE IF NOT EXISTS chapters(
|
||||
chapterId INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
chapterUrl TEXT,
|
||||
novelId INTEGER NOT NULL,
|
||||
chapterName TEXT NOT NULL,
|
||||
releaseDate TEXT,
|
||||
bookmark BOOLEAN NOT NULL DEFAULT 0,
|
||||
\`read\` INTEGER NOT NULL DEFAULT 0,
|
||||
downloaded INTEGER NOT NULL DEFAULT 0,
|
||||
UNIQUE(novelId, chapterUrl),
|
||||
FOREIGN KEY (novelId) REFERENCES novels(novelId)
|
||||
ON DELETE CASCADE
|
||||
)`;
|
||||
|
||||
export const createNovelIdIndexQuery = `CREATE INDEX IF NOT EXISTS chapterNovelIdIndex ON chapters(novelId)`;
|
||||
|
||||
export const createUnreadChaptersIndexQuery = `CREATE INDEX IF NOT EXISTS chapterUnreadByNovelIndex ON chapters(novelId, \`read\`)`;
|
||||
@@ -0,0 +1,11 @@
|
||||
export const createDownloadTableQuery = `
|
||||
CREATE TABLE IF NOT EXISTS downloads(
|
||||
downloadId INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
downloadChapterId INTEGER NOT NULL,
|
||||
chapterName TEXT,
|
||||
chapterText TEXT,
|
||||
nextChapter TEXT,
|
||||
prevChapter TEXT,
|
||||
FOREIGN KEY (downloadChapterId) REFERENCES chapters(chapter_id)
|
||||
ON DELETE CASCADE
|
||||
)`;
|
||||
@@ -0,0 +1,11 @@
|
||||
export const createHistoryTableQuery = `
|
||||
CREATE TABLE IF NOT EXISTS history(
|
||||
historyId INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
historyChapterId INTEGER NOT NULL,
|
||||
historyNovelId INTEGER NOT NULL UNIQUE,
|
||||
historyTimeRead DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (historyChapterId) REFERENCES chapters(chapterId)
|
||||
ON DELETE CASCADE
|
||||
)`;
|
||||
|
||||
export const createChapterIdIndexQuery = `CREATE INDEX IF NOT EXISTS historyChapterIdIndex ON history(historyChapterId)`;
|
||||
@@ -0,0 +1,21 @@
|
||||
export const createNovelTableQuery = `
|
||||
CREATE TABLE IF NOT EXISTS novels(
|
||||
novelId INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
novelUrl TEXT NOT NULL,
|
||||
sourceUrl TEXT UNIQUE NOT NULL,
|
||||
sourceId INTEGER NOT NULL,
|
||||
source TEXT NOT NULL,
|
||||
novelName TEXT NOT NULL,
|
||||
novelCover TEXT NOT NULL,
|
||||
novelSummary TEXT,
|
||||
author TEXT,
|
||||
artist TEXT,
|
||||
status TEXT,
|
||||
genre TEXT,
|
||||
followed INTEGER DEFAULT 0,
|
||||
unread INTEGER DEFAULT 1
|
||||
)`;
|
||||
|
||||
export const createUrlIndexQuery = `CREATE INDEX IF NOT EXISTS novelUrlIndex ON novels(novelUrl)`;
|
||||
|
||||
export const createLibraryIndexQuery = `CREATE INDEX IF NOT EXISTS novelFollowedIndex ON novels(followed)`;
|
||||
@@ -1,10 +1,11 @@
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import {
|
||||
createStackNavigator,
|
||||
TransitionPresets,
|
||||
} from "@react-navigation/stack";
|
||||
import { createMaterialBottomTabNavigator } from "@react-navigation/material-bottom-tabs";
|
||||
import { Provider } from "react-native-paper";
|
||||
import { setStatusBarStyle } from "expo-status-bar";
|
||||
|
||||
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
|
||||
@@ -62,6 +63,7 @@ import { useSelector } from "react-redux";
|
||||
|
||||
const BottomNavigator = () => {
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
|
||||
return (
|
||||
<Tab.Navigator
|
||||
shifting={false}
|
||||
@@ -139,6 +141,10 @@ const BottomNavigator = () => {
|
||||
};
|
||||
|
||||
const Router = () => {
|
||||
const themeCode = useSelector((state) => state.themeReducer.themeCode);
|
||||
|
||||
useEffect(() => setStatusBarStyle(themeCode === 1 ? "dark" : "light"), []);
|
||||
|
||||
return (
|
||||
<Provider>
|
||||
<Stack.Navigator screenOptions={stackNavigatorConfig}>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { GET_EXTENSIONS } from "./types";
|
||||
import { fetchExtensionList } from "../../services/api";
|
||||
|
||||
export const getExtensions = () => async (dispatch) => {
|
||||
const res = await fetchExtensionList();
|
||||
|
||||
dispatch({
|
||||
type: GET_EXTENSIONS,
|
||||
payload: res,
|
||||
});
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
import {
|
||||
UPDATE_NOVEL_HISTORY,
|
||||
CLEAR_NOVEL_HISTORY,
|
||||
GET_HISTORY,
|
||||
LOAD_HISTORY,
|
||||
} from "./types";
|
||||
import {
|
||||
getHistoryFromDb,
|
||||
insertIntoHistory,
|
||||
deleteChapterHistory,
|
||||
} from "../../services/db";
|
||||
|
||||
export const getHistory = () => async (dispatch) => {
|
||||
dispatch({ type: LOAD_HISTORY });
|
||||
|
||||
const history = await getHistoryFromDb();
|
||||
|
||||
dispatch({ type: GET_HISTORY, payload: history });
|
||||
};
|
||||
|
||||
export const updateNovelHistory = (chapterUrl, chapterName, novelUrl) => async (
|
||||
dispatch
|
||||
) => {
|
||||
await insertIntoHistory(chapterUrl, chapterName, novelUrl);
|
||||
|
||||
dispatch({
|
||||
type: UPDATE_NOVEL_HISTORY,
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteChapterFromHistory = (novelUrl) => async (dispatch) => {
|
||||
await deleteChapterHistory(novelUrl);
|
||||
|
||||
dispatch({
|
||||
type: CLEAR_NOVEL_HISTORY,
|
||||
payload: { novelUrl },
|
||||
});
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
import { GET_LIBRARY_NOVELS } from "./types";
|
||||
import { getLibraryFromDb, searchInLibrary } from "../../services/db";
|
||||
|
||||
export const getLibraryNovels = () => async (dispatch) => {
|
||||
const res = await getLibraryFromDb();
|
||||
|
||||
dispatch({
|
||||
type: GET_LIBRARY_NOVELS,
|
||||
payload: res,
|
||||
});
|
||||
};
|
||||
|
||||
export const searchLibraryNovels = (searchText) => async (dispatch) => {
|
||||
const res = await searchInLibrary(searchText);
|
||||
|
||||
dispatch({
|
||||
type: GET_LIBRARY_NOVELS,
|
||||
payload: res,
|
||||
});
|
||||
};
|
||||
@@ -1,172 +0,0 @@
|
||||
import { ToastAndroid } from "react-native";
|
||||
|
||||
import {
|
||||
LOADING_NOVEL,
|
||||
GET_NOVEL,
|
||||
GET_CHAPTERS,
|
||||
FETCHING_NOVEL,
|
||||
SET_NOVEL,
|
||||
UPDATE_IN_LIBRARY,
|
||||
CHAPTER_READ,
|
||||
GET_CHAPTER,
|
||||
CHAPTER_LOADING,
|
||||
CHAPTER_DOWNLOADING,
|
||||
CHAPTER_DOWNLOADED,
|
||||
CHAPTER_DELETED,
|
||||
} from "./types";
|
||||
import {
|
||||
checkNovelInDb,
|
||||
getChaptersFromDb,
|
||||
getNovelInfoFromDb,
|
||||
insertChaptersInDb,
|
||||
insertNovelInfoInDb,
|
||||
toggleFavourite,
|
||||
downloadChapterFromSource,
|
||||
deleteChapterFromDb,
|
||||
chapterRead,
|
||||
isChapterDownloaded,
|
||||
getChapterFromDb,
|
||||
} from "../../services/db";
|
||||
import {
|
||||
fetchChapterFromSource,
|
||||
fetchChaptersFromSource,
|
||||
fetchNovelFromSource,
|
||||
} from "../../services/api";
|
||||
|
||||
export const setNovel = (novel) => async (dispatch) => {
|
||||
dispatch({ type: SET_NOVEL, payload: novel });
|
||||
};
|
||||
|
||||
export const getNovel = (inLibrary, extensionId, novelUrl) => async (
|
||||
dispatch
|
||||
) => {
|
||||
dispatch({ type: LOADING_NOVEL });
|
||||
|
||||
if (inLibrary === 1) {
|
||||
const chapters = await getChaptersFromDb(novelUrl, "", "");
|
||||
dispatch({
|
||||
type: GET_CHAPTERS,
|
||||
payload: chapters,
|
||||
});
|
||||
} else {
|
||||
dispatch({ type: FETCHING_NOVEL });
|
||||
|
||||
const inCache = await checkNovelInDb(novelUrl);
|
||||
|
||||
if (inCache) {
|
||||
const novel = await getNovelInfoFromDb(novelUrl);
|
||||
const chapters = await getChaptersFromDb(novelUrl, "", "");
|
||||
|
||||
dispatch({
|
||||
type: GET_NOVEL,
|
||||
payload: { novel, chapters },
|
||||
});
|
||||
} else {
|
||||
const novel = await fetchNovelFromSource(extensionId, novelUrl);
|
||||
const chapters = await fetchChaptersFromSource(
|
||||
extensionId,
|
||||
novelUrl
|
||||
);
|
||||
|
||||
dispatch({
|
||||
type: GET_NOVEL,
|
||||
payload: { novel, chapters },
|
||||
});
|
||||
|
||||
insertNovelInfoInDb(novel);
|
||||
insertChaptersInDb(novelUrl, chapters);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const sortAndFilterChapters = (novelUrl, filter, sort) => async (
|
||||
dispatch
|
||||
) => {
|
||||
const chapters = await getChaptersFromDb(novelUrl, filter, sort);
|
||||
dispatch({
|
||||
type: GET_CHAPTERS,
|
||||
payload: chapters,
|
||||
});
|
||||
};
|
||||
|
||||
export const insertNovelInLibrary = (inLibrary, novelUrl) => async (
|
||||
dispatch
|
||||
) => {
|
||||
toggleFavourite(inLibrary, novelUrl);
|
||||
|
||||
dispatch({
|
||||
type: UPDATE_IN_LIBRARY,
|
||||
payload: !inLibrary,
|
||||
});
|
||||
|
||||
ToastAndroid.show(
|
||||
!inLibrary ? "Added to library" : "Removed from library",
|
||||
ToastAndroid.SHORT
|
||||
);
|
||||
};
|
||||
|
||||
export const getChapter = (extensionId, chapterUrl, novelUrl) => async (
|
||||
dispatch
|
||||
) => {
|
||||
dispatch({ type: CHAPTER_LOADING });
|
||||
const isDownloaded = await isChapterDownloaded(chapterUrl, novelUrl);
|
||||
let chapter;
|
||||
|
||||
if (isDownloaded) {
|
||||
chapter = await getChapterFromDb(chapterUrl, novelUrl);
|
||||
} else {
|
||||
chapter = await fetchChapterFromSource(
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapterUrl
|
||||
);
|
||||
}
|
||||
dispatch({ type: GET_CHAPTER, payload: chapter });
|
||||
};
|
||||
|
||||
export const updateChapterRead = (chapterUrl, novelUrl) => async (dispatch) => {
|
||||
await chapterRead(chapterUrl, novelUrl);
|
||||
|
||||
dispatch({ type: CHAPTER_READ, payload: { chapterUrl, novelUrl } });
|
||||
};
|
||||
|
||||
// export const downloadChapter = (
|
||||
// extensionId,
|
||||
// novelUrl,
|
||||
// chapterUrl,
|
||||
// chapterName
|
||||
// ) => async (dispatch) => {
|
||||
// dispatch({
|
||||
// type: CHAPTER_DOWNLOADING,
|
||||
// payload: {
|
||||
// extensionId,
|
||||
// novelUrl,
|
||||
// chapterUrl,
|
||||
// },
|
||||
// });
|
||||
|
||||
// await downloadChapterFromSource(extensionId, novelUrl, chapterUrl);
|
||||
|
||||
// dispatch({
|
||||
// type: CHAPTER_DOWNLOADED,
|
||||
// payload: { extensionId, novelUrl, chapterUrl },
|
||||
// });
|
||||
|
||||
// ToastAndroid.show(`Downloaded ${chapterName}`, ToastAndroid.SHORT);
|
||||
// };
|
||||
|
||||
// export const deleteChapter = (
|
||||
// extensionId,
|
||||
// novelUrl,
|
||||
// chapterUrl,
|
||||
// chapterName
|
||||
// ) => (dispatch) => {
|
||||
// deleteChapterFromDb(extensionId, novelUrl, chapterUrl);
|
||||
|
||||
// dispatch({
|
||||
// type: CHAPTER_DELETED,
|
||||
// payload: { extensionId, novelUrl, chapterUrl },
|
||||
// });
|
||||
|
||||
// ToastAndroid.show(`Deleted ${chapterName}`, ToastAndroid.SHORT);
|
||||
// };
|
||||
@@ -0,0 +1,81 @@
|
||||
import { ToastAndroid } from "react-native";
|
||||
|
||||
import {
|
||||
CHAPTER_READ,
|
||||
GET_CHAPTER,
|
||||
CHAPTER_LOADING,
|
||||
CHAPTER_DOWNLOADING,
|
||||
CHAPTER_DOWNLOADED,
|
||||
CHAPTER_DELETED,
|
||||
} from "./chapter.types";
|
||||
|
||||
import { fetchChapter } from "../../source/Source";
|
||||
|
||||
import {
|
||||
markChapterRead,
|
||||
isChapterDownloaded,
|
||||
getChapter,
|
||||
downloadChapter,
|
||||
deleteChapter,
|
||||
} from "../../database/queries/ChapterQueries";
|
||||
|
||||
export const getChapterAction = (
|
||||
extensionId,
|
||||
chapterUrl,
|
||||
novelUrl,
|
||||
novelId
|
||||
) => async (dispatch) => {
|
||||
dispatch({ type: CHAPTER_LOADING });
|
||||
|
||||
const isDownloaded = await isChapterDownloaded(novelId);
|
||||
let chapter;
|
||||
|
||||
if (isDownloaded) {
|
||||
chapter = await getChapter(novelId);
|
||||
} else {
|
||||
chapter = await fetchChapter(extensionId, novelUrl, chapterUrl);
|
||||
}
|
||||
dispatch({ type: GET_CHAPTER, payload: chapter });
|
||||
};
|
||||
|
||||
export const markChapterReadAction = (chapterId) => async (dispatch) => {
|
||||
await markChapterRead(chapterId);
|
||||
dispatch({ type: CHAPTER_READ, payload: { chapterId } });
|
||||
};
|
||||
|
||||
export const downloadChapterAction = (
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapterUrl,
|
||||
chapterName,
|
||||
chapterId
|
||||
) => async (dispatch) => {
|
||||
dispatch({
|
||||
type: CHAPTER_DOWNLOADING,
|
||||
payload: {
|
||||
chapterId,
|
||||
},
|
||||
});
|
||||
|
||||
await downloadChapter(extensionId, novelUrl, chapterUrl, chapterId);
|
||||
|
||||
dispatch({
|
||||
type: CHAPTER_DOWNLOADED,
|
||||
payload: { chapterId },
|
||||
});
|
||||
|
||||
ToastAndroid.show(`Downloaded ${chapterName}`, ToastAndroid.SHORT);
|
||||
};
|
||||
|
||||
export const deleteChapterAction = (chapterId, chapterName) => async (
|
||||
dispatch
|
||||
) => {
|
||||
await deleteChapter(chapterId);
|
||||
|
||||
dispatch({
|
||||
type: CHAPTER_DELETED,
|
||||
payload: { chapterId },
|
||||
});
|
||||
|
||||
ToastAndroid.show(`Deleted ${chapterName}`, ToastAndroid.SHORT);
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
import {
|
||||
CHAPTER_READ,
|
||||
GET_CHAPTER,
|
||||
CHAPTER_LOADING,
|
||||
CHAPTER_DOWNLOADING,
|
||||
CHAPTER_DOWNLOADED,
|
||||
CHAPTER_DELETED,
|
||||
} from "./chapter.types";
|
||||
|
||||
const initialState = {
|
||||
chapter: null,
|
||||
downloading: [],
|
||||
loading: true,
|
||||
};
|
||||
|
||||
const chapterReducer = (state = initialState, action) => {
|
||||
const { type, payload } = action;
|
||||
|
||||
switch (type) {
|
||||
case CHAPTER_READ:
|
||||
return {
|
||||
...state,
|
||||
chapters: state.chapters.map((chapter) =>
|
||||
chapter.chapterId === payload.chapterId
|
||||
? { ...chapter, read: 1 }
|
||||
: chapter
|
||||
),
|
||||
};
|
||||
case CHAPTER_LOADING:
|
||||
return {
|
||||
...state,
|
||||
chapterLoading: true,
|
||||
};
|
||||
case GET_CHAPTER:
|
||||
return { ...state, chapter: payload, chapterLoading: false };
|
||||
case CHAPTER_DOWNLOADING:
|
||||
return {
|
||||
...state,
|
||||
downloading: payload.chapterId,
|
||||
};
|
||||
case CHAPTER_DOWNLOADED:
|
||||
return {
|
||||
...state,
|
||||
downloading: null,
|
||||
chapters: state.chapters.map((chapter) =>
|
||||
chapter.chapterId === payload.chapterId
|
||||
? { ...chapter, downloaded: 1 }
|
||||
: chapter
|
||||
),
|
||||
};
|
||||
case CHAPTER_DELETED:
|
||||
return {
|
||||
...state,
|
||||
chapters: state.chapters.map((chapter) =>
|
||||
chapter.chapterId === payload.chapterId
|
||||
? { ...chapter, downloaded: 0 }
|
||||
: chapter
|
||||
),
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default chapterReducer;
|
||||
@@ -0,0 +1,6 @@
|
||||
export const CHAPTER_DOWNLOADED = "CHAPTER_DOWNLOADED";
|
||||
export const CHAPTER_DELETED = "CHAPTER_DELETED";
|
||||
export const CHAPTER_DOWNLOADING = "CHAPTER_DOWNLOADING";
|
||||
export const CHAPTER_READ = "CHAPTER_READ";
|
||||
export const CHAPTER_LOADING = "CHAPTER_LOADING";
|
||||
export const GET_CHAPTER = "GET_CHAPTER";
|
||||
@@ -0,0 +1,36 @@
|
||||
import {
|
||||
UPDATE_NOVEL_HISTORY,
|
||||
CLEAR_NOVEL_HISTORY,
|
||||
GET_HISTORY,
|
||||
LOAD_HISTORY,
|
||||
} from "./history.types";
|
||||
import {
|
||||
getHistory,
|
||||
insertHistory,
|
||||
deleteHistory,
|
||||
} from "../../database/queries/HistoryQueries";
|
||||
|
||||
export const getHistoryAction = () => async (dispatch) => {
|
||||
dispatch({ type: LOAD_HISTORY });
|
||||
|
||||
const history = await getHistory();
|
||||
|
||||
dispatch({ type: GET_HISTORY, payload: history });
|
||||
};
|
||||
|
||||
export const insertHistoryAction = (novelId, chapterId) => async (dispatch) => {
|
||||
await insertHistory(novelId, chapterId);
|
||||
|
||||
dispatch({
|
||||
type: UPDATE_NOVEL_HISTORY,
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteHistoryAction = (novelId) => async (dispatch) => {
|
||||
await deleteHistory(novelId);
|
||||
|
||||
dispatch({
|
||||
type: CLEAR_NOVEL_HISTORY,
|
||||
payload: { novelId },
|
||||
});
|
||||
};
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
GET_HISTORY,
|
||||
LOAD_HISTORY,
|
||||
UPDATE_NOVEL_HISTORY,
|
||||
} from "../actions/types";
|
||||
} from "./history.types";
|
||||
|
||||
const initialState = {
|
||||
history: [],
|
||||
@@ -24,7 +24,7 @@ const historyReducer = (state = initialState, action) => {
|
||||
return {
|
||||
...state,
|
||||
history: state.history.filter(
|
||||
(item) => item.novelUrl !== payload.novelUrl
|
||||
(item) => item.novelId !== payload.novelId
|
||||
),
|
||||
};
|
||||
// return {
|
||||
@@ -0,0 +1,4 @@
|
||||
export const LOAD_HISTORY = "LOAD_HISTORY";
|
||||
export const GET_HISTORY = "GET_HISTORY";
|
||||
export const UPDATE_NOVEL_HISTORY = "UPDATE_NOVEL_HISTORY";
|
||||
export const CLEAR_NOVEL_HISTORY = "CLEAR_NOVEL_HISTORY";
|
||||
@@ -0,0 +1,24 @@
|
||||
import { GET_LIBRARY_NOVELS } from "./library.types";
|
||||
import {
|
||||
getLibrary,
|
||||
searchLibrary,
|
||||
} from "../../database/queries/LibraryQueries";
|
||||
|
||||
export const getLibraryAction = () => async (dispatch) => {
|
||||
const res = await getLibrary();
|
||||
|
||||
dispatch({
|
||||
type: GET_LIBRARY_NOVELS,
|
||||
payload: res,
|
||||
});
|
||||
};
|
||||
|
||||
export const searchLibraryAction = (searchText) => async (dispatch) => {
|
||||
console.log(searchText);
|
||||
const res = await searchLibrary(searchText);
|
||||
|
||||
dispatch({
|
||||
type: GET_LIBRARY_NOVELS,
|
||||
payload: res,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import { FOLLOW_NOVEL, UNFOLLOW_NOVEL } from "../novel/novel.types";
|
||||
import { GET_LIBRARY_NOVELS } from "./library.types";
|
||||
|
||||
const initialState = {
|
||||
novels: [],
|
||||
loading: true,
|
||||
};
|
||||
|
||||
const libraryReducer = (state = initialState, action) => {
|
||||
const { type, payload } = action;
|
||||
|
||||
switch (type) {
|
||||
case GET_LIBRARY_NOVELS:
|
||||
return { novels: payload, loading: false };
|
||||
case FOLLOW_NOVEL:
|
||||
return {
|
||||
...state,
|
||||
novels: [...state.novels, { ...payload, followed: 1 }],
|
||||
};
|
||||
case UNFOLLOW_NOVEL:
|
||||
return {
|
||||
...state,
|
||||
novels: state.novels.filter(
|
||||
(novel) => novel.novelId !== payload
|
||||
),
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default libraryReducer;
|
||||
@@ -0,0 +1 @@
|
||||
export const GET_LIBRARY_NOVELS = "GET_LIBRARY_NOVELS";
|
||||
@@ -0,0 +1,212 @@
|
||||
import { ToastAndroid } from "react-native";
|
||||
|
||||
import {
|
||||
LOADING_NOVEL,
|
||||
GET_NOVEL,
|
||||
GET_CHAPTERS,
|
||||
FETCHING_NOVEL,
|
||||
SET_NOVEL,
|
||||
UPDATE_IN_LIBRARY,
|
||||
CHAPTER_READ,
|
||||
GET_CHAPTER,
|
||||
CHAPTER_LOADING,
|
||||
CHAPTER_DOWNLOADING,
|
||||
CHAPTER_DOWNLOADED,
|
||||
CHAPTER_DELETED,
|
||||
UPDATE_NOVEL,
|
||||
UPDATE_LAST_READ,
|
||||
FOLLOW_NOVEL,
|
||||
UNFOLLOW_NOVEL,
|
||||
} from "./novel.types";
|
||||
|
||||
import { updateNovel } from "../../services/updates";
|
||||
import { fetchChapter, fetchNovel } from "../../source/Source";
|
||||
import {
|
||||
followNovel,
|
||||
insertNovel,
|
||||
checkNovelInCache,
|
||||
getNovel,
|
||||
} from "../../database/queries/NovelQueries";
|
||||
import {
|
||||
getChapters,
|
||||
insertChapters,
|
||||
markChapterRead,
|
||||
isChapterDownloaded,
|
||||
getChapter,
|
||||
downloadChapter,
|
||||
deleteChapter,
|
||||
getLastReadChapter,
|
||||
} from "../../database/queries/ChapterQueries";
|
||||
|
||||
export const setNovel = (novel) => async (dispatch) => {
|
||||
dispatch({ type: SET_NOVEL, payload: novel });
|
||||
};
|
||||
|
||||
export const getNovelAction = (followed, sourceId, novelUrl, novelId) => async (
|
||||
dispatch
|
||||
) => {
|
||||
dispatch({ type: LOADING_NOVEL });
|
||||
|
||||
if (followed === 1) {
|
||||
const chapters = await getChapters(novelId);
|
||||
|
||||
dispatch({
|
||||
type: GET_CHAPTERS,
|
||||
payload: chapters,
|
||||
});
|
||||
} else {
|
||||
dispatch({ type: FETCHING_NOVEL });
|
||||
|
||||
const inCache = await checkNovelInCache(novelUrl);
|
||||
|
||||
if (inCache) {
|
||||
const novel = await getNovel(novelUrl);
|
||||
novel.chapters = await getChapters(novel.novelId);
|
||||
dispatch({
|
||||
type: GET_NOVEL,
|
||||
payload: novel,
|
||||
});
|
||||
} else {
|
||||
/**
|
||||
* Fetch novel from source
|
||||
*/
|
||||
const fetchedNovel = await fetchNovel(sourceId, novelUrl);
|
||||
|
||||
/**
|
||||
* Insert novel in db
|
||||
*/
|
||||
const fetchedNovelId = await insertNovel(fetchedNovel);
|
||||
await insertChapters(fetchedNovelId, fetchedNovel.chapters);
|
||||
|
||||
/**
|
||||
* Get Novel from db
|
||||
*/
|
||||
const novel = await getNovel(novelUrl);
|
||||
novel.chapters = await getChapters(novel.novelId);
|
||||
|
||||
dispatch({
|
||||
type: GET_NOVEL,
|
||||
payload: novel,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const sortAndFilterChapters = (novelUrl, filter, sort) => async (
|
||||
dispatch
|
||||
) => {
|
||||
// const chapters = await getChaptersFromDb(novelUrl, filter, sort);
|
||||
// dispatch({
|
||||
// type: GET_CHAPTERS,
|
||||
// payload: chapters,
|
||||
// });
|
||||
};
|
||||
|
||||
export const followNovelAction = (novel) => async (dispatch) => {
|
||||
await followNovel(novel.followed, novel.novelId);
|
||||
|
||||
if (!novel.followed) {
|
||||
dispatch({
|
||||
type: FOLLOW_NOVEL,
|
||||
payload: novel,
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: UNFOLLOW_NOVEL,
|
||||
payload: novel.novelId,
|
||||
});
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: UPDATE_IN_LIBRARY,
|
||||
payload: !novel.followed,
|
||||
});
|
||||
|
||||
ToastAndroid.show(
|
||||
!novel.followed ? "Added to library" : "Removed from library",
|
||||
ToastAndroid.SHORT
|
||||
);
|
||||
};
|
||||
|
||||
export const getChapterAction = (
|
||||
extensionId,
|
||||
chapterUrl,
|
||||
novelUrl,
|
||||
novelId
|
||||
) => async (dispatch) => {
|
||||
dispatch({ type: CHAPTER_LOADING });
|
||||
|
||||
const isDownloaded = await isChapterDownloaded(novelId);
|
||||
let chapter;
|
||||
|
||||
if (isDownloaded) {
|
||||
chapter = await getChapter(novelId);
|
||||
} else {
|
||||
chapter = await fetchChapter(extensionId, novelUrl, chapterUrl);
|
||||
}
|
||||
dispatch({ type: GET_CHAPTER, payload: chapter });
|
||||
};
|
||||
|
||||
export const markChapterReadAction = (chapterId) => async (dispatch) => {
|
||||
await markChapterRead(chapterId);
|
||||
dispatch({ type: CHAPTER_READ, payload: { chapterId } });
|
||||
};
|
||||
|
||||
export const downloadChapterAction = (
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapterUrl,
|
||||
chapterName,
|
||||
chapterId
|
||||
) => async (dispatch) => {
|
||||
dispatch({
|
||||
type: CHAPTER_DOWNLOADING,
|
||||
payload: {
|
||||
chapterId,
|
||||
},
|
||||
});
|
||||
|
||||
await downloadChapter(extensionId, novelUrl, chapterUrl, chapterId);
|
||||
|
||||
dispatch({
|
||||
type: CHAPTER_DOWNLOADED,
|
||||
payload: { chapterId },
|
||||
});
|
||||
|
||||
ToastAndroid.show(`Downloaded ${chapterName}`, ToastAndroid.SHORT);
|
||||
};
|
||||
|
||||
export const deleteChapterAction = (chapterId, chapterName) => async (
|
||||
dispatch
|
||||
) => {
|
||||
await deleteChapter(chapterId);
|
||||
|
||||
dispatch({
|
||||
type: CHAPTER_DELETED,
|
||||
payload: { chapterId },
|
||||
});
|
||||
|
||||
ToastAndroid.show(`Deleted ${chapterName}`, ToastAndroid.SHORT);
|
||||
};
|
||||
|
||||
export const updateNovelAction = (extensionId, novelUrl, novelId) => async (
|
||||
dispatch
|
||||
) => {
|
||||
dispatch({ type: FETCHING_NOVEL });
|
||||
|
||||
const updatedNovel = await updateNovel(extensionId, novelUrl, novelId);
|
||||
|
||||
dispatch({
|
||||
type: UPDATE_NOVEL,
|
||||
payload: { novel: updatedNovel, chapters: updatedNovel.chapters },
|
||||
});
|
||||
};
|
||||
|
||||
export const getLastReadChapterAction = (novelId) => async (dispatch) => {
|
||||
const lastReadChapter = await getLastReadChapter(novelId);
|
||||
|
||||
dispatch({
|
||||
type: UPDATE_LAST_READ,
|
||||
payload: lastReadChapter,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,126 @@
|
||||
import {
|
||||
CHAPTER_DOWNLOADING,
|
||||
CHAPTER_DOWNLOADED,
|
||||
CHAPTER_DELETED,
|
||||
GET_CHAPTERS,
|
||||
LOADING_NOVEL,
|
||||
GET_NOVEL,
|
||||
GET_CHAPTER,
|
||||
FETCHING_NOVEL,
|
||||
SET_NOVEL,
|
||||
UPDATE_IN_LIBRARY,
|
||||
CHAPTER_READ,
|
||||
CHAPTER_LOADING,
|
||||
UPDATE_NOVEL,
|
||||
UPDATE_LAST_READ,
|
||||
} from "./novel.types";
|
||||
|
||||
const initialState = {
|
||||
novel: null,
|
||||
chapters: [],
|
||||
chapter: null,
|
||||
downloading: [],
|
||||
chapterLoading: true,
|
||||
loading: true,
|
||||
fetching: false,
|
||||
};
|
||||
|
||||
const novelReducer = (state = initialState, action) => {
|
||||
const { type, payload } = action;
|
||||
|
||||
switch (type) {
|
||||
case LOADING_NOVEL:
|
||||
return { ...state, loading: true };
|
||||
case FETCHING_NOVEL:
|
||||
return { ...state, fetching: true };
|
||||
case SET_NOVEL:
|
||||
return { ...state, novel: payload };
|
||||
case GET_NOVEL:
|
||||
return {
|
||||
...state,
|
||||
novel: payload,
|
||||
chapters: payload.chapters,
|
||||
loading: false,
|
||||
fetching: false,
|
||||
};
|
||||
case GET_CHAPTERS:
|
||||
return {
|
||||
...state,
|
||||
chapters: payload,
|
||||
loading: false,
|
||||
fetching: false,
|
||||
};
|
||||
case UPDATE_IN_LIBRARY:
|
||||
return {
|
||||
...state,
|
||||
novel: { ...state.novel, followed: payload },
|
||||
};
|
||||
case UPDATE_NOVEL:
|
||||
return {
|
||||
...state,
|
||||
novel: {
|
||||
...state.novel,
|
||||
novelName: payload.novel.novelName,
|
||||
novelUrl: payload.novel.novelUrl,
|
||||
novelSummary: payload.novel.novelSummary,
|
||||
author: payload.novel.author,
|
||||
artist: payload.novel.artist,
|
||||
genre: payload.novel.genre,
|
||||
status: payload.novel.status,
|
||||
},
|
||||
chapters: payload.chapters,
|
||||
loading: false,
|
||||
fetching: false,
|
||||
};
|
||||
case CHAPTER_READ:
|
||||
return {
|
||||
...state,
|
||||
chapters: state.chapters.map((chapter) =>
|
||||
chapter.chapterId === payload.chapterId
|
||||
? { ...chapter, read: 1 }
|
||||
: chapter
|
||||
),
|
||||
};
|
||||
case CHAPTER_LOADING:
|
||||
return {
|
||||
...state,
|
||||
chapterLoading: true,
|
||||
};
|
||||
case GET_CHAPTER:
|
||||
return { ...state, chapter: payload, chapterLoading: false };
|
||||
case CHAPTER_DOWNLOADING:
|
||||
return {
|
||||
...state,
|
||||
downloading: payload.chapterId,
|
||||
};
|
||||
case CHAPTER_DOWNLOADED:
|
||||
return {
|
||||
...state,
|
||||
downloading: null,
|
||||
chapters: state.chapters.map((chapter) =>
|
||||
chapter.chapterId === payload.chapterId
|
||||
? { ...chapter, downloaded: 1 }
|
||||
: chapter
|
||||
),
|
||||
};
|
||||
case CHAPTER_DELETED:
|
||||
return {
|
||||
...state,
|
||||
chapters: state.chapters.map((chapter) =>
|
||||
chapter.chapterId === payload.chapterId
|
||||
? { ...chapter, downloaded: 0 }
|
||||
: chapter
|
||||
),
|
||||
};
|
||||
|
||||
case UPDATE_LAST_READ:
|
||||
return {
|
||||
...state,
|
||||
novel: { ...state.novel, lastRead: payload },
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default novelReducer;
|
||||
@@ -1,47 +1,19 @@
|
||||
export const SWITCH_THEME = "SWITCH_THEME";
|
||||
|
||||
export const GET_LIBRARY_NOVELS = "GET_LIBRARY_NOVELS";
|
||||
|
||||
export const SET_DISPLAY_MODE = "SET_DISPLAY_MODE";
|
||||
|
||||
export const SET_ITEMS_PER_ROW = "SET_ITEMS_PER_ROW";
|
||||
|
||||
export const GET_EXTENSIONS = "GET_EXTENSIONS";
|
||||
|
||||
export const LOADING_NOVEL = "LOADING_NOVEL";
|
||||
|
||||
export const FETCHING_NOVEL = "FETCHING_NOVEL";
|
||||
|
||||
export const LOADING_CHAPTERS = "LOADING_CHAPTERS";
|
||||
|
||||
export const SET_NOVEL = "SET_NOVEL";
|
||||
|
||||
export const GET_NOVEL = "GET_NOVEL";
|
||||
|
||||
export const SET_NOVEL = "SET_NOVEL";
|
||||
export const UPDATE_NOVEL = "UPDATE_NOVEL";
|
||||
export const UPDATE_IN_LIBRARY = "UPDATE_IN_LIBRARY";
|
||||
|
||||
export const GET_CHAPTERS = "GET_CHAPTERS";
|
||||
export const UPDATE_LAST_READ = "UPDATE_LAST_READ";
|
||||
|
||||
export const CHAPTER_DOWNLOADED = "CHAPTER_DOWNLOADED";
|
||||
|
||||
export const CHAPTER_DELETED = "CHAPTER_DELETED";
|
||||
|
||||
export const CHAPTER_DOWNLOADING = "CHAPTER_DOWNLOADING";
|
||||
|
||||
export const LOAD_HISTORY = "LOAD_HISTORY";
|
||||
|
||||
export const UPDATE_NOVEL_HISTORY = "UPDATE_NOVEL_HISTORY";
|
||||
|
||||
export const CLEAR_NOVEL_HISTORY = "CLEAR_NOVEL_HISTORY";
|
||||
|
||||
export const GET_HISTORY = "GET_HISTORY";
|
||||
|
||||
export const CHAPTER_READ = "CHAPTER_READ";
|
||||
|
||||
export const CHAPTER_LOADING = "CHAPTER_LOADING";
|
||||
export const GET_CHAPTER = "GET_CHAPTER";
|
||||
|
||||
export const CHAPTER_LOADING = "CHAPTER_LOADING";
|
||||
|
||||
export const UPDATE_READER_THEME = "UPDATE_READER_THEME";
|
||||
|
||||
export const UPDATE_READER_TEXT_SIZE = "UPDATE_READER_TEXT_SIZE";
|
||||
export const FOLLOW_NOVEL = "FOLLOW_NOVEL";
|
||||
export const UNFOLLOW_NOVEL = "UNFOLLOW_NOVEL";
|
||||
@@ -1,17 +0,0 @@
|
||||
const initialState = {
|
||||
novels: [],
|
||||
loading: true,
|
||||
};
|
||||
|
||||
const libraryReducer = (state = initialState, action) => {
|
||||
const { type, payload } = action;
|
||||
|
||||
switch (type) {
|
||||
case "GET_LIBRARY_NOVELS":
|
||||
return { novels: payload, loading: false };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default libraryReducer;
|
||||
@@ -1,108 +0,0 @@
|
||||
import {
|
||||
CHAPTER_DOWNLOADING,
|
||||
CHAPTER_DOWNLOADED,
|
||||
CHAPTER_DELETED,
|
||||
GET_CHAPTERS,
|
||||
LOADING_NOVEL,
|
||||
GET_NOVEL,
|
||||
GET_CHAPTER,
|
||||
FETCHING_NOVEL,
|
||||
SET_NOVEL,
|
||||
UPDATE_IN_LIBRARY,
|
||||
CHAPTER_READ,
|
||||
CHAPTER_LOADING,
|
||||
} from "../actions/types";
|
||||
|
||||
const initialState = {
|
||||
novel: null,
|
||||
chapters: [],
|
||||
chapter: null,
|
||||
chapterLoading: true,
|
||||
loading: true,
|
||||
fetching: false,
|
||||
};
|
||||
|
||||
const novelReducer = (state = initialState, action) => {
|
||||
const { type, payload } = action;
|
||||
|
||||
switch (type) {
|
||||
case LOADING_NOVEL:
|
||||
return { ...state, loading: true };
|
||||
case FETCHING_NOVEL:
|
||||
return { ...state, loading: true, fetching: true };
|
||||
case SET_NOVEL:
|
||||
return { ...state, novel: payload };
|
||||
case GET_NOVEL:
|
||||
return {
|
||||
...state,
|
||||
novel: payload.novel,
|
||||
chapters: payload.chapters,
|
||||
loading: false,
|
||||
fetching: false,
|
||||
};
|
||||
case GET_CHAPTERS:
|
||||
return {
|
||||
...state,
|
||||
chapters: payload,
|
||||
loading: false,
|
||||
fetching: false,
|
||||
};
|
||||
case UPDATE_IN_LIBRARY:
|
||||
return {
|
||||
...state,
|
||||
novel: { ...state.novel, libraryStatus: payload },
|
||||
};
|
||||
|
||||
case CHAPTER_READ:
|
||||
return {
|
||||
...state,
|
||||
chapters: state.chapters.map((chapter) =>
|
||||
chapter.chapterUrl === payload.chapterUrl &&
|
||||
chapter.novelUrl === payload.novelUrl
|
||||
? { ...chapter, read: 1 }
|
||||
: chapter
|
||||
),
|
||||
};
|
||||
case CHAPTER_LOADING:
|
||||
return {
|
||||
...state,
|
||||
chapterLoading: true,
|
||||
};
|
||||
case GET_CHAPTER:
|
||||
return { ...state, chapter: payload, chapterLoading: false };
|
||||
// case CHAPTER_DOWNLOADING:
|
||||
// return {
|
||||
// ...state,
|
||||
// chapters: [
|
||||
// state.chapters.map((chap) =>
|
||||
// chap.novelUrl === payload.novelUrl &&
|
||||
// chap.chapterUrl === payload.novelUrl
|
||||
// ? { ...chap, downloading: true, downloaded: false }
|
||||
// : chap
|
||||
// ),
|
||||
// ],
|
||||
// };
|
||||
// case CHAPTER_DOWNLOADED:
|
||||
// return {
|
||||
// ...state,
|
||||
// downloads: state.downloads.map((chap) =>
|
||||
// chap.chapterUrl === payload.chapterUrl &&
|
||||
// chap.novelUrl === payload.novelUrl
|
||||
// ? { ...chap, downloading: false, downloaded: true }
|
||||
// : chap
|
||||
// ),
|
||||
// };
|
||||
// case CHAPTER_DELETED:
|
||||
// return {
|
||||
// downloads: state.downloads.filter(
|
||||
// (chapter) =>
|
||||
// chapter.chapterUrl === payload.chapterUrl &&
|
||||
// chapter.novelUrl === payload.novelUrl
|
||||
// ),
|
||||
// };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default novelReducer;
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
SET_ITEMS_PER_ROW,
|
||||
UPDATE_READER_TEXT_SIZE,
|
||||
UPDATE_READER_THEME,
|
||||
} from "./types";
|
||||
} from "./settings.types";
|
||||
|
||||
export const setDisplayMode = (val) => (dispatch) => {
|
||||
dispatch({
|
||||
@@ -13,6 +13,7 @@ export const setDisplayMode = (val) => (dispatch) => {
|
||||
};
|
||||
|
||||
export const setItemsPerRow = (val) => (dispatch) => {
|
||||
console.log("called" + val);
|
||||
dispatch({
|
||||
type: SET_ITEMS_PER_ROW,
|
||||
payload: val,
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
SET_ITEMS_PER_ROW,
|
||||
UPDATE_READER_TEXT_SIZE,
|
||||
UPDATE_READER_THEME,
|
||||
} from "../actions/types";
|
||||
} from "./settings.types";
|
||||
|
||||
const initialState = {
|
||||
displayMode: 0,
|
||||
@@ -34,9 +34,16 @@ const settingsReducer = (state = initialState, action) => {
|
||||
|
||||
switch (type) {
|
||||
case SET_DISPLAY_MODE:
|
||||
return { ...state, displayMode: payload };
|
||||
return {
|
||||
...state,
|
||||
displayMode: payload,
|
||||
itemsPerRow: payload === 2 ? 1 : state.itemsPerRow,
|
||||
};
|
||||
case SET_ITEMS_PER_ROW:
|
||||
return { ...state, itemsPerRow: payload };
|
||||
return {
|
||||
...state,
|
||||
itemsPerRow: state.displayMode !== 2 ? payload : 1,
|
||||
};
|
||||
case UPDATE_READER_THEME:
|
||||
return { ...state, reader: { ...state.reader, theme: payload } };
|
||||
case UPDATE_READER_TEXT_SIZE:
|
||||
@@ -0,0 +1,4 @@
|
||||
export const SET_DISPLAY_MODE = "SET_DISPLAY_MODE";
|
||||
export const SET_ITEMS_PER_ROW = "SET_ITEMS_PER_ROW";
|
||||
export const UPDATE_READER_THEME = "UPDATE_READER_THEME";
|
||||
export const UPDATE_READER_TEXT_SIZE = "UPDATE_READER_TEXT_SIZE";
|
||||
@@ -0,0 +1,11 @@
|
||||
import { GET_SOURCES } from "./source.types";
|
||||
import { fetchSources } from "../../source/Source";
|
||||
|
||||
export const getSourcesAction = () => async (dispatch) => {
|
||||
const res = await fetchSources();
|
||||
|
||||
dispatch({
|
||||
type: GET_SOURCES,
|
||||
payload: res,
|
||||
});
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { GET_EXTENSIONS } from "../actions/types";
|
||||
import { GET_SOURCES } from "./source.types";
|
||||
|
||||
const initialState = {
|
||||
extensions: [],
|
||||
@@ -9,7 +9,7 @@ const libraryReducer = (state = initialState, action) => {
|
||||
const { type, payload } = action;
|
||||
|
||||
switch (type) {
|
||||
case GET_EXTENSIONS:
|
||||
case GET_SOURCES:
|
||||
return { extensions: payload, loading: false };
|
||||
default:
|
||||
return state;
|
||||
@@ -0,0 +1 @@
|
||||
export const GET_SOURCES = "GET_SOURCES";
|
||||
@@ -3,12 +3,12 @@ import { persistStore, persistReducer } from "redux-persist";
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
import thunk from "redux-thunk";
|
||||
|
||||
import themeReducer from "../reducers/theme";
|
||||
import settingsReducer from "../reducers/settings";
|
||||
import libraryReducer from "../reducers/library";
|
||||
import extensionReducer from "../reducers/extension";
|
||||
import novelReducer from "../reducers/novel";
|
||||
import historyReducer from "../reducers/history";
|
||||
import themeReducer from "./theme/theme.reducer";
|
||||
import settingsReducer from "./settings/settings.reducer";
|
||||
import libraryReducer from "./library/library.reducer";
|
||||
import extensionReducer from "./source/source.reducers";
|
||||
import novelReducer from "./novel/novel.reducer";
|
||||
import historyReducer from "./history/history.reducer";
|
||||
|
||||
const persistConfig = {
|
||||
key: "root",
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SWITCH_THEME } from "./types";
|
||||
import { SWITCH_THEME } from "./theme.types";
|
||||
|
||||
/**
|
||||
* 0 -> Amoled Dark Theme
|
||||
@@ -0,0 +1 @@
|
||||
export const SWITCH_THEME = "SWITCH_THEME";
|
||||
@@ -3,13 +3,13 @@ import { StyleSheet, View, FlatList, ActivityIndicator } from "react-native";
|
||||
import { Appbar } from "../../components/common/Appbar";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { getExtensions } from "../../redux/actions/extension";
|
||||
import { getSourcesAction } from "../../redux/source/source.actions";
|
||||
|
||||
import ExtensionCard from "./components/ExtensionCard";
|
||||
|
||||
const Browse = ({ theme, extensions, loading, getExtensions }) => {
|
||||
const Browse = ({ theme, extensions, loading, getSourcesAction }) => {
|
||||
useEffect(() => {
|
||||
getExtensions();
|
||||
getSourcesAction();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -46,7 +46,7 @@ const mapStateToProps = (state) => ({
|
||||
loading: state.extensionReducer.loading,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, { getExtensions })(Browse);
|
||||
export default connect(mapStateToProps, { getSourcesAction })(Browse);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
|
||||
@@ -17,6 +17,7 @@ const ExtensionCard = ({ item }) => {
|
||||
style={styles.extensionCard}
|
||||
onPress={() => navigation.navigate(sourceName + "Stack")}
|
||||
rippleColor={theme.rippleColor}
|
||||
borderless
|
||||
>
|
||||
<>
|
||||
<Image
|
||||
@@ -74,7 +75,7 @@ const styles = StyleSheet.create({
|
||||
marginVertical: 4,
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 20,
|
||||
borderRadius: 6,
|
||||
borderRadius: 4,
|
||||
},
|
||||
extensionIcon: {
|
||||
width: 40,
|
||||
|
||||
@@ -6,8 +6,11 @@ import { CollapsibleHeaderScrollView } from "react-native-collapsible-header-vie
|
||||
|
||||
import BottomSheet from "./components/BottomSheet";
|
||||
|
||||
import { updateNovelHistory } from "../../redux/actions/history";
|
||||
import { getChapter, updateChapterRead } from "../../redux/actions/novel";
|
||||
import { insertHistoryAction } from "../../redux/history/history.actions";
|
||||
import {
|
||||
getChapterAction,
|
||||
markChapterReadAction,
|
||||
} from "../../redux/novel/novel.actions";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
|
||||
@@ -15,20 +18,26 @@ const ChapterItem = ({
|
||||
route,
|
||||
navigation,
|
||||
theme,
|
||||
updateNovelHistory,
|
||||
updateChapterRead,
|
||||
getChapter,
|
||||
insertHistoryAction,
|
||||
markChapterReadAction,
|
||||
getChapterAction,
|
||||
chapter,
|
||||
loading,
|
||||
reader,
|
||||
}) => {
|
||||
const { extensionId, chapterUrl, novelUrl, chapterName } = route.params;
|
||||
const {
|
||||
chapterId,
|
||||
extensionId,
|
||||
chapterUrl,
|
||||
novelUrl,
|
||||
novelId,
|
||||
} = route.params;
|
||||
|
||||
let _panel = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
getChapter(extensionId, chapterUrl, novelUrl);
|
||||
updateNovelHistory(chapterUrl, chapterName, novelUrl);
|
||||
getChapterAction(extensionId, chapterUrl, novelUrl, chapterId);
|
||||
insertHistoryAction(novelId, chapterId);
|
||||
}, []);
|
||||
|
||||
const readerBackground = (val) => {
|
||||
@@ -42,7 +51,7 @@ const ChapterItem = ({
|
||||
};
|
||||
|
||||
const readerTextColor = (val) => {
|
||||
const textColor = val === 1 ? theme.textColorSecondary : "#000000";
|
||||
const textColor = val === 1 ? "rgba(255,255,255,0.7)" : "#000000";
|
||||
|
||||
return textColor;
|
||||
};
|
||||
@@ -71,7 +80,7 @@ const ChapterItem = ({
|
||||
return (
|
||||
<Provider>
|
||||
<CollapsibleHeaderScrollView
|
||||
headerContainerBackgroundColor={"rgba(0,0,0,0.4)"}
|
||||
headerContainerBackgroundColor="rgba(0,0,0,0.4)"
|
||||
CollapsibleHeaderComponent={
|
||||
<Appbar.Header
|
||||
style={{
|
||||
@@ -138,7 +147,7 @@ const ChapterItem = ({
|
||||
]}
|
||||
onScroll={({ nativeEvent }) => {
|
||||
if (isCloseToBottom(nativeEvent)) {
|
||||
updateChapterRead(chapterUrl, novelUrl);
|
||||
markChapterReadAction(chapterId);
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -179,9 +188,9 @@ const mapStateToProps = (state) => ({
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
updateNovelHistory,
|
||||
updateChapterRead,
|
||||
getChapter,
|
||||
insertHistoryAction,
|
||||
markChapterReadAction,
|
||||
getChapterAction,
|
||||
})(ChapterItem);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
||||
@@ -10,7 +10,7 @@ import { connect } from "react-redux";
|
||||
import {
|
||||
updateReaderTextSize,
|
||||
updateReaderTheme,
|
||||
} from "../../../redux/actions/settings";
|
||||
} from "../../../redux/settings/settings.actions";
|
||||
import BottomSheetHandle from "../../../components/common/BottomSheetHandle";
|
||||
|
||||
const ChapterBottomSheet = ({
|
||||
@@ -28,12 +28,7 @@ const ChapterBottomSheet = ({
|
||||
snappingPoints={[0, 200]}
|
||||
showBackdrop={false}
|
||||
>
|
||||
<View
|
||||
style={[
|
||||
styles.contentContainer,
|
||||
{ backgroundColor: "rgba(0,0,0,0.4)" },
|
||||
]}
|
||||
>
|
||||
<View style={styles.contentContainer}>
|
||||
<BottomSheetHandle />
|
||||
<View style={styles.readerSettingsContainer}>
|
||||
<Text
|
||||
@@ -118,6 +113,9 @@ export default connect(mapStateToProps, {
|
||||
const styles = StyleSheet.create({
|
||||
contentContainer: {
|
||||
flex: 1,
|
||||
borderTopLeftRadius: 8,
|
||||
borderTopRightRadius: 8,
|
||||
backgroundColor: "rgba(0,0,0,0.4)",
|
||||
},
|
||||
readerSettingsContainer: { flex: 1, alignItems: "center", paddingTop: 30 },
|
||||
});
|
||||
|
||||
@@ -10,28 +10,28 @@ import { connect } from "react-redux";
|
||||
import EmptyView from "../../components/common/EmptyView";
|
||||
|
||||
import {
|
||||
getHistory,
|
||||
deleteChapterFromHistory,
|
||||
} from "../../redux/actions/history";
|
||||
getHistoryAction,
|
||||
deleteHistoryAction,
|
||||
} from "../../redux/history/history.actions";
|
||||
|
||||
const History = ({
|
||||
navigation,
|
||||
theme,
|
||||
history,
|
||||
loading,
|
||||
getHistory,
|
||||
deleteChapterFromHistory,
|
||||
getHistoryAction,
|
||||
deleteHistoryAction,
|
||||
}) => {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
getHistory();
|
||||
}, [])
|
||||
getHistoryAction();
|
||||
}, [getHistoryAction])
|
||||
);
|
||||
|
||||
const renderHistoryCard = ({ item }) => (
|
||||
<HistoryCard
|
||||
item={item}
|
||||
deleteHistory={deleteChapterFromHistory}
|
||||
deleteHistoryAction={deleteHistoryAction}
|
||||
navigation={navigation}
|
||||
/>
|
||||
);
|
||||
@@ -79,8 +79,8 @@ const mapStateToProps = (state) => ({
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
getHistory,
|
||||
deleteChapterFromHistory,
|
||||
getHistoryAction,
|
||||
deleteHistoryAction,
|
||||
})(History);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
||||
@@ -7,7 +7,7 @@ import moment from "moment";
|
||||
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
const HistoryCard = ({ item, deleteHistory, navigation }) => {
|
||||
const HistoryCard = ({ item, deleteHistoryAction, navigation }) => {
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
|
||||
return (
|
||||
@@ -21,7 +21,6 @@ const HistoryCard = ({ item, deleteHistory, navigation }) => {
|
||||
novelCover: item.novelCover,
|
||||
extensionId: item.extensionId,
|
||||
novelUrl: item.novelUrl,
|
||||
libraryStatus: item.libraryStatus,
|
||||
})
|
||||
}
|
||||
>
|
||||
@@ -67,7 +66,7 @@ const HistoryCard = ({ item, deleteHistory, navigation }) => {
|
||||
style={{
|
||||
marginRight: 0,
|
||||
}}
|
||||
onPress={() => deleteHistory(item.novelUrl)}
|
||||
onPress={() => deleteHistoryAction(item.novelId)}
|
||||
/>
|
||||
<IconButton
|
||||
icon="play"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useCallback } from "react";
|
||||
import React, { useState, useCallback, useEffect } from "react";
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
@@ -15,31 +15,29 @@ import NovelCover from "../../components/common/NovelCover";
|
||||
import EmptyView from "../../components/common/EmptyView";
|
||||
|
||||
import {
|
||||
getLibraryNovels,
|
||||
searchLibraryNovels,
|
||||
} from "../../redux/actions/library";
|
||||
getLibraryAction,
|
||||
searchLibraryAction,
|
||||
} from "../../redux/library/library.actions";
|
||||
import { SearchAppbar } from "../../components/common/Appbar";
|
||||
import { setNovel } from "../../redux/actions/novel";
|
||||
import { setNovel } from "../../redux/novel/novel.actions";
|
||||
|
||||
const LibraryScreen = ({
|
||||
navigation,
|
||||
theme,
|
||||
novels,
|
||||
loading,
|
||||
getLibraryNovels,
|
||||
searchLibraryNovels,
|
||||
getLibraryAction,
|
||||
searchLibraryAction,
|
||||
setNovel,
|
||||
itemsPerRow,
|
||||
}) => {
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
|
||||
const [searchText, setSearchText] = useState("");
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
setSearchText("");
|
||||
getLibraryNovels();
|
||||
}, [])
|
||||
);
|
||||
useEffect(() => {
|
||||
getLibraryAction();
|
||||
}, [getLibraryAction]);
|
||||
|
||||
/**
|
||||
* TODO: fix refreshing
|
||||
@@ -47,7 +45,7 @@ const LibraryScreen = ({
|
||||
const onRefresh = () => {
|
||||
ToastAndroid.show("Updating library", ToastAndroid.SHORT);
|
||||
setRefreshing(true);
|
||||
getLibraryNovels();
|
||||
getLibraryAction();
|
||||
setRefreshing(false);
|
||||
};
|
||||
|
||||
@@ -67,8 +65,8 @@ const LibraryScreen = ({
|
||||
<SearchAppbar
|
||||
screen="Library"
|
||||
placeholder="Search Library"
|
||||
getNovels={getLibraryNovels}
|
||||
getSearchResults={searchLibraryNovels}
|
||||
getNovels={getLibraryAction}
|
||||
getSearchResults={searchLibraryAction}
|
||||
searchText={searchText}
|
||||
setSearchText={setSearchText}
|
||||
/>
|
||||
@@ -83,7 +81,8 @@ const LibraryScreen = ({
|
||||
flexGrow: 1,
|
||||
paddingBottom: 8,
|
||||
}}
|
||||
numColumns={3}
|
||||
numColumns={itemsPerRow}
|
||||
key={itemsPerRow}
|
||||
data={novels}
|
||||
keyExtractor={(item) => item.novelUrl}
|
||||
renderItem={({ item }) => (
|
||||
@@ -126,13 +125,14 @@ const LibraryScreen = ({
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
theme: state.themeReducer.theme,
|
||||
itemsPerRow: state.settingsReducer.itemsPerRow,
|
||||
novels: state.libraryReducer.novels,
|
||||
loading: state.libraryReducer.loading,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
getLibraryNovels,
|
||||
searchLibraryNovels,
|
||||
getLibraryAction,
|
||||
searchLibraryAction,
|
||||
setNovel,
|
||||
})(LibraryScreen);
|
||||
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
import React from "react";
|
||||
import { StyleSheet, TextInput } from "react-native";
|
||||
import { Appbar } from "react-native-paper";
|
||||
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
const LibraryAppbar = ({
|
||||
search,
|
||||
setSearch,
|
||||
getLibraryNovels,
|
||||
searchLibraryNovels,
|
||||
}) => {
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
|
||||
const { searching, searchText } = search;
|
||||
|
||||
return (
|
||||
<Appbar.Header style={{ backgroundColor: theme.colorPrimary }}>
|
||||
{!searching ? (
|
||||
<>
|
||||
<Appbar.Content
|
||||
title="Library"
|
||||
titleStyle={{ color: theme.textColorPrimary }}
|
||||
/>
|
||||
<Appbar.Action
|
||||
icon="magnify"
|
||||
onPress={() =>
|
||||
setSearch({ ...search, searching: true })
|
||||
}
|
||||
color={theme.textColorPrimary}
|
||||
/>
|
||||
<Appbar.Action
|
||||
color={theme.textColorPrimary}
|
||||
icon="filter-variant"
|
||||
disabled
|
||||
// onPress={() => _panel.show({ velocity: -1.5 })}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Appbar.BackAction
|
||||
onPress={() => {
|
||||
setSearch({ searching: false, searchText: "" });
|
||||
getLibraryNovels();
|
||||
}}
|
||||
color={theme.textColorPrimary}
|
||||
size={24}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="Search..."
|
||||
defaultValue={searchText}
|
||||
style={[
|
||||
styles.searchBar,
|
||||
{
|
||||
color: theme.textColorPrimary,
|
||||
},
|
||||
]}
|
||||
autoFocus
|
||||
placeholderTextColor={theme.textColorHintDark}
|
||||
blurOnSubmit={true}
|
||||
onChangeText={(text) => {
|
||||
setSearch({ ...search, searchText: text });
|
||||
searchLibraryNovels(text);
|
||||
}}
|
||||
/>
|
||||
{searchText !== "" && (
|
||||
<Appbar.Action
|
||||
icon="close"
|
||||
onPress={() => {
|
||||
setSearch({ ...search, searchText: "" });
|
||||
getLibraryNovels();
|
||||
}}
|
||||
color={theme.textColorPrimary}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Appbar.Header>
|
||||
);
|
||||
};
|
||||
|
||||
export default LibraryAppbar;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
searchBar: { fontSize: 18, flex: 1, marginLeft: 15 },
|
||||
});
|
||||
@@ -24,7 +24,7 @@ const AboutScreen = ({ navigation }) => {
|
||||
titleStyle={{ color: theme.textColorPrimary }}
|
||||
title="Version"
|
||||
descriptionStyle={{ color: theme.textColorSecondary }}
|
||||
description="Stable 1.0.6"
|
||||
description="Stable 1.0.10"
|
||||
/>
|
||||
<List.Item
|
||||
titleStyle={{ color: theme.textColorPrimary }}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import React, { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { View, Text } from "react-native";
|
||||
import { setStatusBarStyle } from "expo-status-bar";
|
||||
import { List, Modal, Portal } from "react-native-paper";
|
||||
import InputSpinner from "react-native-input-spinner";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { switchTheme } from "../../redux/actions/theme";
|
||||
import { setDisplayMode, setItemsPerRow } from "../../redux/actions/settings";
|
||||
import { switchTheme } from "../../redux/theme/theme.actions";
|
||||
import {
|
||||
setDisplayMode,
|
||||
setItemsPerRow,
|
||||
} from "../../redux/settings/settings.actions";
|
||||
|
||||
import { Appbar } from "../../components/common/Appbar";
|
||||
import {
|
||||
@@ -13,23 +17,24 @@ import {
|
||||
ThemeCheckbox,
|
||||
} from "../../components/settings/Checkbox";
|
||||
|
||||
import {
|
||||
deleteHistory,
|
||||
deleteNovelsNotInLibrary,
|
||||
deleteDatabase,
|
||||
} from "../../services/db";
|
||||
import { deleteAllHistory } from "../../database/queries/HistoryQueries";
|
||||
import { deleteNovelCache } from "../../database/queries/NovelQueries";
|
||||
// import { deleteDb } from "../../database/DBHelper";
|
||||
|
||||
const SettingsScreen = ({
|
||||
navigation,
|
||||
theme,
|
||||
themeCode,
|
||||
displayMode,
|
||||
itemsPerRow,
|
||||
switchTheme,
|
||||
setDisplayMode,
|
||||
setItemsPerRow,
|
||||
}) => {
|
||||
const display = {
|
||||
0: "Compact",
|
||||
1: "Comfortable",
|
||||
0: "Compact Grid",
|
||||
1: "Comfortable Grid",
|
||||
2: "List",
|
||||
};
|
||||
|
||||
const themes = [
|
||||
@@ -59,6 +64,10 @@ const SettingsScreen = ({
|
||||
const showthemeModal = () => setthemeModalVisible(true);
|
||||
const hidethemeModal = () => setthemeModalVisible(false);
|
||||
|
||||
const [itemsModalVisible, setitemsModalVisible] = useState(false);
|
||||
const showitemsModal = () => setitemsModalVisible(true);
|
||||
const hideitemsModal = () => setitemsModalVisible(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Appbar title="Settings" onBackAction={() => navigation.goBack()} />
|
||||
@@ -82,7 +91,7 @@ const SettingsScreen = ({
|
||||
title="Clear database"
|
||||
descriptionStyle={desciptionStyles}
|
||||
description="Delete history for novels not in your library"
|
||||
onPress={() => deleteNovelsNotInLibrary()}
|
||||
onPress={() => deleteNovelCache()}
|
||||
rippleColor={theme.rippleColor}
|
||||
/>
|
||||
<List.Item
|
||||
@@ -90,7 +99,7 @@ const SettingsScreen = ({
|
||||
title="Clear history"
|
||||
descriptionStyle={desciptionStyles}
|
||||
description="Delete reading history for all novels"
|
||||
onPress={() => deleteHistory()}
|
||||
onPress={() => deleteAllHistory()}
|
||||
rippleColor={theme.rippleColor}
|
||||
/>
|
||||
<List.Subheader
|
||||
@@ -130,8 +139,66 @@ const SettingsScreen = ({
|
||||
displayMode={displayMode}
|
||||
onPress={() => setDisplayMode(0)}
|
||||
/>
|
||||
<DisplayCheckbox
|
||||
value={2}
|
||||
displayMode={displayMode}
|
||||
onPress={() => setDisplayMode(2)}
|
||||
/>
|
||||
</Modal>
|
||||
</Portal>
|
||||
<List.Item
|
||||
titleStyle={titleStyles}
|
||||
title="Items per row in library"
|
||||
descriptionStyle={desciptionStyles}
|
||||
description={itemsPerRow}
|
||||
onPress={showitemsModal}
|
||||
rippleColor={theme.rippleColor}
|
||||
/>
|
||||
<Portal>
|
||||
<Modal
|
||||
visible={itemsModalVisible}
|
||||
onDismiss={hideitemsModal}
|
||||
contentContainerStyle={{
|
||||
backgroundColor: theme.colorPrimaryDark,
|
||||
padding: 20,
|
||||
margin: 20,
|
||||
borderRadius: 6,
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.textColorPrimary,
|
||||
fontSize: 18,
|
||||
marginBottom: 10,
|
||||
}}
|
||||
>
|
||||
Items per row
|
||||
</Text>
|
||||
<InputSpinner
|
||||
style={{
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 16,
|
||||
elevation: 0,
|
||||
}}
|
||||
max={10}
|
||||
min={1}
|
||||
skin="paper"
|
||||
background={theme.colorPrimaryDark}
|
||||
colorMax={theme.colorAccentDark}
|
||||
colorLeft={theme.colorAccentDark}
|
||||
colorRight={theme.colorAccentDark}
|
||||
buttonTextColor={theme.textColorPrimary}
|
||||
colorPress={"white"}
|
||||
colorMin={theme.colorAccentDark}
|
||||
value={itemsPerRow}
|
||||
onChange={(num) => setItemsPerRow(num)}
|
||||
inputStyle={{
|
||||
color: theme.textColorPrimary,
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
</Portal>
|
||||
|
||||
{/* <List.Subheader
|
||||
style={{
|
||||
color: theme.colorAccentDark,
|
||||
@@ -146,9 +213,8 @@ const SettingsScreen = ({
|
||||
descriptionStyle={desciptionStyles}
|
||||
description="Delete entire database"
|
||||
rippleColor={theme.rippleColor}
|
||||
onPress={() => deleteDatabase()}
|
||||
onPress={() => deleteDb()}
|
||||
/> */}
|
||||
|
||||
<List.Subheader
|
||||
style={{
|
||||
color: theme.colorAccentDark,
|
||||
@@ -203,6 +269,7 @@ const mapStateToProps = (state) => ({
|
||||
themeCode: state.themeReducer.themeCode,
|
||||
theme: state.themeReducer.theme,
|
||||
displayMode: state.settingsReducer.displayMode,
|
||||
itemsPerRow: state.settingsReducer.itemsPerRow,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
|
||||
+25
-61
@@ -1,26 +1,19 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import {
|
||||
StyleSheet,
|
||||
View,
|
||||
FlatList,
|
||||
RefreshControl,
|
||||
ToastAndroid,
|
||||
} from "react-native";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { StyleSheet, View, FlatList, RefreshControl } from "react-native";
|
||||
import { Provider, Portal } from "react-native-paper";
|
||||
|
||||
import ChapterCard from "./components/ChapterCard";
|
||||
import NovelInfoHeader from "./components/NovelHeader";
|
||||
import { BottomSheet } from "./components/BottomSheet";
|
||||
|
||||
import { downloadOrDeleteChapter } from "../../services/db";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import {
|
||||
getNovel,
|
||||
insertNovelInLibrary,
|
||||
getNovelAction,
|
||||
followNovelAction,
|
||||
sortAndFilterChapters,
|
||||
} from "../../redux/actions/novel";
|
||||
updateNovelAction,
|
||||
} from "../../redux/novel/novel.actions";
|
||||
|
||||
const Novel = ({
|
||||
route,
|
||||
@@ -28,70 +21,39 @@ const Novel = ({
|
||||
novel,
|
||||
chapters,
|
||||
loading,
|
||||
getNovel,
|
||||
getNovelAction,
|
||||
fetching,
|
||||
insertNovelInLibrary,
|
||||
followNovelAction,
|
||||
sortAndFilterChapters,
|
||||
updateNovelAction,
|
||||
}) => {
|
||||
const {
|
||||
extensionId,
|
||||
sourceId,
|
||||
novelUrl,
|
||||
novelName,
|
||||
novelCover,
|
||||
libraryStatus,
|
||||
followed,
|
||||
novelId,
|
||||
} = route.params;
|
||||
|
||||
const [downloading, setDownloading] = useState({
|
||||
downloading: false,
|
||||
chapterUrl: "",
|
||||
});
|
||||
|
||||
let _panel = useRef(null); // Bottomsheet ref
|
||||
|
||||
/**
|
||||
* Download chapter from source
|
||||
*
|
||||
* If downloaded then delete from db
|
||||
*/
|
||||
|
||||
const downloadChapter = (
|
||||
downloadStatus,
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapterUrl,
|
||||
chapterName
|
||||
) => {
|
||||
setDownloading({ downloading: true, chapterUrl: chapterUrl });
|
||||
downloadOrDeleteChapter(
|
||||
downloadStatus,
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapterUrl
|
||||
).then((res) => {
|
||||
setDownloading({ downloading: false });
|
||||
ToastAndroid.show(
|
||||
!downloadStatus
|
||||
? `Downloaded ${chapterName}`
|
||||
: `Deleted ${chapterName}`,
|
||||
ToastAndroid.SHORT
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getNovel(libraryStatus, extensionId, novelUrl);
|
||||
getNovelAction(followed, sourceId, novelUrl, novelId);
|
||||
}, []);
|
||||
|
||||
const renderChapterCard = ({ item }) => (
|
||||
<ChapterCard
|
||||
novelUrl={novelUrl}
|
||||
extensionId={extensionId}
|
||||
extensionId={sourceId}
|
||||
chapter={item}
|
||||
downloadChapter={downloadChapter}
|
||||
downloading={downloading}
|
||||
/>
|
||||
);
|
||||
|
||||
const onRefresh = () => {
|
||||
updateNovelAction(sourceId, novelUrl, novelId);
|
||||
};
|
||||
|
||||
return (
|
||||
<Provider>
|
||||
<View
|
||||
@@ -113,16 +75,17 @@ const Novel = ({
|
||||
theme={theme}
|
||||
item={{ novelName, novelCover }}
|
||||
novel={novel}
|
||||
noOfChapters={chapters.length}
|
||||
insertNovelInLibrary={insertNovelInLibrary}
|
||||
noOfChapters={chapters?.length}
|
||||
followNovelAction={followNovelAction}
|
||||
loading={loading}
|
||||
bottomSheetRef={_panel}
|
||||
firstChapter={chapters[0]}
|
||||
/>
|
||||
}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={fetching}
|
||||
// onRefresh={onRefresh}
|
||||
onRefresh={onRefresh}
|
||||
colors={[theme.textColorPrimary]}
|
||||
progressBackgroundColor={theme.colorPrimary}
|
||||
/>
|
||||
@@ -148,9 +111,10 @@ const mapStateToProps = (state) => ({
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
getNovel,
|
||||
insertNovelInLibrary,
|
||||
getNovelAction,
|
||||
followNovelAction,
|
||||
sortAndFilterChapters,
|
||||
updateNovelAction,
|
||||
})(Novel);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
||||
@@ -16,12 +16,12 @@ export const BottomSheet = ({
|
||||
|
||||
const sortChapters = (sort) => {
|
||||
setChapterFlags({ ...chapterFlags, sort: sort });
|
||||
sortAndFilterChapters(novelUrl, chapterFlags.filter, chapterFlags.sort);
|
||||
sortAndFilterChapters(novelUrl, chapterFlags.filter, sort);
|
||||
};
|
||||
|
||||
const filterChapters = (filter) => {
|
||||
setChapterFlags({ ...chapterFlags, filter: filter });
|
||||
sortAndFilterChapters(novelUrl, chapterFlags.filter, chapterFlags.sort);
|
||||
sortAndFilterChapters(novelUrl, filter, chapterFlags.sort);
|
||||
};
|
||||
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
@@ -53,7 +53,6 @@ export const BottomSheet = ({
|
||||
<RadioButton.Group
|
||||
onValueChange={(newValue) => sortChapters(newValue)}
|
||||
value={chapterFlags.sort}
|
||||
c
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import React, { useState } from "react";
|
||||
import React from "react";
|
||||
import { View, Text, ActivityIndicator, StyleSheet } from "react-native";
|
||||
import { TouchableRipple, IconButton } from "react-native-paper";
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
|
||||
import { useSelector } from "react-redux";
|
||||
import {
|
||||
deleteChapterAction,
|
||||
downloadChapterAction,
|
||||
} from "../../../redux/novel/novel.actions";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
const ChapterCard = ({
|
||||
novelUrl,
|
||||
chapter,
|
||||
downloadChapter,
|
||||
extensionId,
|
||||
theme,
|
||||
downloading,
|
||||
downloadChapterAction,
|
||||
deleteChapterAction,
|
||||
}) => {
|
||||
const [downloaded, setDownloaded] = useState(chapter.downloaded);
|
||||
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
|
||||
const navigation = useNavigation();
|
||||
|
||||
return (
|
||||
@@ -23,10 +25,12 @@ const ChapterCard = ({
|
||||
style={styles.chapterCardContainer}
|
||||
onPress={() =>
|
||||
navigation.navigate("ChapterItem", {
|
||||
chapterId: chapter.chapterId,
|
||||
chapterUrl: chapter.chapterUrl,
|
||||
extensionId,
|
||||
novelUrl: novelUrl,
|
||||
chapterName: chapter.chapterName,
|
||||
novelId: chapter.novelId,
|
||||
})
|
||||
}
|
||||
rippleColor={theme.rippleColor}
|
||||
@@ -65,8 +69,7 @@ const ChapterCard = ({
|
||||
)}
|
||||
</View>
|
||||
<View>
|
||||
{downloading.downloading &&
|
||||
downloading.chapterUrl === chapter.chapterUrl ? (
|
||||
{downloading === chapter.chapterId ? (
|
||||
<ActivityIndicator
|
||||
color={theme.textColorHintDark}
|
||||
size={25}
|
||||
@@ -75,26 +78,32 @@ const ChapterCard = ({
|
||||
) : (
|
||||
<IconButton
|
||||
icon={
|
||||
downloaded
|
||||
chapter.downloaded
|
||||
? "check-circle"
|
||||
: "arrow-down-circle-outline"
|
||||
}
|
||||
animated
|
||||
color={
|
||||
downloaded
|
||||
chapter.downloaded
|
||||
? theme.textColorPrimary
|
||||
: theme.textColorHintDark
|
||||
}
|
||||
size={25}
|
||||
onPress={() => {
|
||||
downloadChapter(
|
||||
downloaded ? 1 : 0,
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapter.chapterUrl,
|
||||
chapter.chapterName
|
||||
);
|
||||
setDownloaded(!downloaded);
|
||||
if (!chapter.downloaded) {
|
||||
downloadChapterAction(
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapter.chapterUrl,
|
||||
chapter.chapterName,
|
||||
chapter.chapterId
|
||||
);
|
||||
} else {
|
||||
deleteChapterAction(
|
||||
chapter.chapterId,
|
||||
chapter.chapterName
|
||||
);
|
||||
}
|
||||
}}
|
||||
style={{ margin: 2 }}
|
||||
/>
|
||||
@@ -105,7 +114,15 @@ const ChapterCard = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default ChapterCard;
|
||||
const mapStateToProps = (state) => ({
|
||||
theme: state.themeReducer.theme,
|
||||
downloading: state.novelReducer.downloading,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
downloadChapterAction,
|
||||
deleteChapterAction,
|
||||
})(ChapterCard);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
chapterCardContainer: {
|
||||
|
||||
@@ -19,15 +19,16 @@ const NovelInfoHeader = ({
|
||||
item,
|
||||
novel,
|
||||
noOfChapters,
|
||||
insertNovelInLibrary,
|
||||
followNovelAction,
|
||||
loading,
|
||||
bottomSheetRef,
|
||||
theme,
|
||||
firstChapter,
|
||||
}) => {
|
||||
const navigation = useNavigation();
|
||||
|
||||
const [more, setMore] = useState(
|
||||
!loading && novel.libraryStatus !== 1 ? true : false
|
||||
!loading && novel.followed !== 1 ? true : false
|
||||
);
|
||||
|
||||
const renderGenreChip = ({ item }) => (
|
||||
@@ -81,8 +82,9 @@ const NovelInfoHeader = ({
|
||||
fontSize: 14,
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
numberOfLines={2}
|
||||
>
|
||||
{novel["Author(s)"].replace(",", ", ")}
|
||||
{novel.author.replace(",", ", ")}
|
||||
</Text>
|
||||
|
||||
<Text
|
||||
@@ -93,7 +95,7 @@ const NovelInfoHeader = ({
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{novel["Status"]}
|
||||
{novel.status}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
@@ -118,7 +120,7 @@ const NovelInfoHeader = ({
|
||||
icon={() => (
|
||||
<MaterialCommunityIcons
|
||||
name={
|
||||
novel.libraryStatus
|
||||
novel.followed
|
||||
? "heart"
|
||||
: "heart-outline"
|
||||
}
|
||||
@@ -126,12 +128,7 @@ const NovelInfoHeader = ({
|
||||
color={theme.colorAccentDark}
|
||||
/>
|
||||
)}
|
||||
onPress={() =>
|
||||
insertNovelInLibrary(
|
||||
novel.libraryStatus,
|
||||
novel.novelUrl
|
||||
)
|
||||
}
|
||||
onPress={() => followNovelAction(novel)}
|
||||
style={[
|
||||
{ backgroundColor: theme.colorPrimaryDark },
|
||||
styles.toggleFavourite,
|
||||
@@ -141,9 +138,7 @@ const NovelInfoHeader = ({
|
||||
color: theme.textColorPrimary,
|
||||
}}
|
||||
>
|
||||
{novel.libraryStatus
|
||||
? "In Library"
|
||||
: "Add to library"}
|
||||
{novel.followed ? "In Library" : "Add to library"}
|
||||
</Chip>
|
||||
|
||||
<IconButton
|
||||
@@ -217,7 +212,7 @@ const NovelInfoHeader = ({
|
||||
}
|
||||
contentContainerStyle={styles.genreContainer}
|
||||
horizontal
|
||||
data={novel["Genre(s)"] && novel["Genre(s)"].split(",")}
|
||||
data={novel.genre && novel.genre.split(",")}
|
||||
keyExtractor={(item) => item}
|
||||
renderItem={renderGenreChip}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
@@ -231,18 +226,24 @@ const NovelInfoHeader = ({
|
||||
]}
|
||||
uppercase={false}
|
||||
labelStyle={{ letterSpacing: 0 }}
|
||||
onPress={() =>
|
||||
navigation.navigate("ChapterItem", {
|
||||
chapterUrl: novel.lastRead,
|
||||
novelUrl: novel.novelUrl,
|
||||
extensionId: novel.extensionId,
|
||||
chapterName: novel.lastReadName,
|
||||
})
|
||||
}
|
||||
// onPress={() => {
|
||||
// console.log(firstChapter.chapterName);
|
||||
// navigation.navigate("ChapterItem", {
|
||||
// chapterUrl:
|
||||
// novel.lastRead.chapterUrl ??
|
||||
// firstChapter.chapterUrl,
|
||||
// novelUrl: novel.novelUrl,
|
||||
// extensionId: novel.extensionId,
|
||||
// chapterName:
|
||||
// novel.lastRead.chapterUrl ??
|
||||
// firstChapter.chapterName,
|
||||
// });
|
||||
// }}
|
||||
>
|
||||
{novel.unread === 1
|
||||
? `Start reading ${novel.lastReadName}`
|
||||
: `Continue reading ${novel.lastReadName}`}
|
||||
{/* {novel.lastRead
|
||||
? `Continue reading ${novel.lastRead.chapterName}`
|
||||
: `Start reading ${firstChapter.chapterName}`} */}
|
||||
Start reading
|
||||
</Button>
|
||||
|
||||
<TouchableRipple
|
||||
|
||||
@@ -10,6 +10,9 @@ import { useSelector } from "react-redux";
|
||||
|
||||
const BoxNovel = ({ navigation }) => {
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
const itemsPerRow = useSelector(
|
||||
(state) => state.settingsReducer.itemsPerRow
|
||||
);
|
||||
|
||||
const library = useSelector((state) => state.libraryReducer.novels);
|
||||
|
||||
@@ -83,7 +86,8 @@ const BoxNovel = ({ navigation }) => {
|
||||
) : (
|
||||
<FlatList
|
||||
contentContainerStyle={styles.list}
|
||||
numColumns={3}
|
||||
numColumns={itemsPerRow}
|
||||
key={itemsPerRow}
|
||||
data={novels}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyExtractor={(item) => item.novelUrl}
|
||||
@@ -91,7 +95,12 @@ const BoxNovel = ({ navigation }) => {
|
||||
<NovelCover
|
||||
item={item}
|
||||
onPress={() => {
|
||||
navigation.navigate("NovelItem", item);
|
||||
navigation.navigate("NovelItem", {
|
||||
novelName: item.novelName,
|
||||
novelCover: item.novelCover,
|
||||
novelUrl: item.novelUrl,
|
||||
sourceId: item.extensionId,
|
||||
});
|
||||
}}
|
||||
libraryStatus={
|
||||
checkIFInLibrary(item.novelUrl)
|
||||
|
||||
@@ -11,6 +11,9 @@ import EmptyView from "../../../components/common/EmptyView";
|
||||
const ReadLightNovel = ({ navigation }) => {
|
||||
const theme = useSelector((state) => state.themeReducer.theme);
|
||||
const library = useSelector((state) => state.libraryReducer.novels);
|
||||
const itemsPerRow = useSelector(
|
||||
(state) => state.settingsReducer.itemsPerRow
|
||||
);
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
@@ -75,7 +78,8 @@ const ReadLightNovel = ({ navigation }) => {
|
||||
) : (
|
||||
<FlatList
|
||||
contentContainerStyle={{ flexGrow: 1 }}
|
||||
numColumns={3}
|
||||
numColumns={itemsPerRow}
|
||||
key={itemsPerRow}
|
||||
data={novels}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyExtractor={(item) => item.novelUrl}
|
||||
@@ -83,7 +87,12 @@ const ReadLightNovel = ({ navigation }) => {
|
||||
<NovelCover
|
||||
item={item}
|
||||
onPress={() =>
|
||||
navigation.navigate("NovelItem", item)
|
||||
navigation.navigate("NovelItem", {
|
||||
novelName: item.novelName,
|
||||
novelCover: item.novelCover,
|
||||
novelUrl: item.novelUrl,
|
||||
sourceId: item.extensionId,
|
||||
})
|
||||
}
|
||||
libraryStatus={
|
||||
checkIFInLibrary(item.novelUrl)
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
import { setStatusBarStyle } from "expo-status-bar";
|
||||
|
||||
export const saveReaderTheme = async (val) => {
|
||||
AsyncStorage.setItem("@reader_theme", JSON.stringify(val));
|
||||
};
|
||||
|
||||
export const getReaderTheme = async () => {
|
||||
let theme = await AsyncStorage.getItem("@reader_theme").then((value) => {
|
||||
if (value) return JSON.parse(value);
|
||||
});
|
||||
|
||||
return theme;
|
||||
};
|
||||
|
||||
export const setStatusBar = () => {
|
||||
AsyncStorage.getItem("@application_theme").then((value) => {
|
||||
if (JSON.parse(value) === "lightTheme") {
|
||||
setStatusBarStyle("dark");
|
||||
} else {
|
||||
setStatusBarStyle("light");
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -24,6 +24,7 @@ export const fetchNovelFromSource = async (extensionId, novelUrl) => {
|
||||
lastRead: res.novelChapters[0].chapterUrl,
|
||||
lastReadName: res.novelChapters[0].chapterName,
|
||||
extensionId: res.extensionId,
|
||||
novelChapters: res.novelChapters,
|
||||
};
|
||||
|
||||
return novel;
|
||||
|
||||
@@ -1,473 +0,0 @@
|
||||
import * as SQLite from "expo-sqlite";
|
||||
const db = SQLite.openDatabase("lnreader.db");
|
||||
|
||||
/**
|
||||
* Create tables
|
||||
*/
|
||||
|
||||
export const createTables = async () => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
"CREATE TABLE IF NOT EXISTS LibraryTable (novelId INTEGER PRIMARY KEY AUTOINCREMENT,novelUrl VARCHAR(255) UNIQUE, novelName VARCHAR(255), novelCover VARCHAR(255), novelSummary TEXT, `Author(s)` VARCHAR(255), `Genre(s)` VARCHAR(255), Status VARCHAR(255), extensionId INTEGER, libraryStatus INTEGER DEFAULT 0, unread INTEGER DEFAULT 1, lastRead VARCHAR(255), lastReadName VARCHAR(255), sourceUrl VARCHAR(255), source VARCHAR(255))",
|
||||
null,
|
||||
(tx, results) => console.log("Library Table Created"),
|
||||
(tx, error) => console.log(error)
|
||||
);
|
||||
|
||||
tx.executeSql(
|
||||
"CREATE TABLE IF NOT EXISTS ChapterTable (chapterId INTEGER PRIMARY KEY AUTOINCREMENT, chapterUrl VARCHAR(255), chapterName VARCHAR(255), releaseDate VARCHAR(255), `read` INTEGER DEFAULT 0, downloaded INTEGER DEFAULT 0,novelUrl VARCHAR(255), FOREIGN KEY (novelUrl) REFERENCES LibraryTable(novelUrl) ON DELETE CASCADE)",
|
||||
null,
|
||||
(tx, results) => console.log("Chapter Table Created"),
|
||||
(tx, error) => console.log(error)
|
||||
);
|
||||
tx.executeSql(
|
||||
"CREATE TABLE IF NOT EXISTS HistoryTable (historyId INTEGER PRIMARY KEY AUTOINCREMENT, chapterUrl VARCHAR(255), novelUrl VARCHAR(255) UNIQUE, chapterName VARCHAR(255), lastRead DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (novelUrl) REFERENCES LibraryTable(novelUrl) ON DELETE CASCADE)",
|
||||
null,
|
||||
(tx, results) => console.log("History Table Created"),
|
||||
(tx, error) => console.log(error)
|
||||
);
|
||||
|
||||
tx.executeSql(
|
||||
"CREATE TABLE IF NOT EXISTS DownloadsTable (downloadId INTEGER PRIMARY KEY AUTOINCREMENT, chapterUrl VARCHAR(255), novelUrl VARCHAR(255), chapterName VARCHAR(255), chapterText VARCHAR(255), nextChapter VARCHAR(255), prevChapter VARCHAR(255), FOREIGN KEY (novelUrl) REFERENCES LibraryTable(novelUrl) ON DELETE CASCADE, UNIQUE(chapterUrl, novelUrl))",
|
||||
null,
|
||||
(tx, results) => console.log("Downloads Table Created"),
|
||||
(tx, error) => console.log(error)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Query to create index novelUrl and chapterUrl
|
||||
*/
|
||||
|
||||
export const createIndexes = async () => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
"CREATE INDEX IF NOT EXISTS LibraryTable_novelUrl ON LibraryTable(novelUrl)",
|
||||
null,
|
||||
(tx, results) => console.log("Library Index Created"),
|
||||
(tx, error) => console.log(error)
|
||||
);
|
||||
|
||||
tx.executeSql(
|
||||
"CREATE INDEX IF NOT EXISTS ChapterTable_novelUrl ON ChapterTable(novelUrl)",
|
||||
null,
|
||||
(tx, results) => console.log("Chapter Index Created"),
|
||||
(tx, error) => console.log(error)
|
||||
);
|
||||
tx.executeSql(
|
||||
"CREATE INDEX IF NOT EXISTS HistoryTable_novelUrl ON HistoryTable(novelUrl)",
|
||||
null,
|
||||
(tx, results) => console.log("History Index Created"),
|
||||
(tx, error) => console.log(error)
|
||||
);
|
||||
|
||||
tx.executeSql(
|
||||
"CREATE INDEX IF NOT EXISTS DownloadsTable_chapterUrl ON DownloadsTable(chapterUrl)",
|
||||
null,
|
||||
(tx, results) => console.log("Downloads Index Created"),
|
||||
(tx, error) => console.log(error)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const insertNovelInfoInDb = async (novel) => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
"INSERT INTO LibraryTable (novelUrl, novelName, novelCover, novelSummary, `Author(s)`, `Genre(s)`, Status, extensionId, lastRead, lastReadName, sourceUrl, source) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
[
|
||||
novel.novelUrl,
|
||||
novel.novelName,
|
||||
novel.novelCover,
|
||||
novel.novelSummary,
|
||||
novel["Author(s)"],
|
||||
novel["Genre(s)"],
|
||||
novel.Status,
|
||||
novel.extensionId,
|
||||
novel.lastRead,
|
||||
novel.lastReadName,
|
||||
novel.sourceUrl,
|
||||
novel.source,
|
||||
],
|
||||
(txObj, res) => {},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const insertChaptersInDb = async (novelUrl, chapters) => {
|
||||
db.transaction((tx) => {
|
||||
chapters.map((chapter) =>
|
||||
tx.executeSql(
|
||||
"INSERT INTO ChapterTable (chapterUrl, chapterName, releaseDate, novelUrl) values (?, ?, ?, ?)",
|
||||
[
|
||||
chapter.chapterUrl,
|
||||
chapter.chapterName,
|
||||
chapter.releaseDate,
|
||||
novelUrl,
|
||||
],
|
||||
(txObj, res) => {},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get novel info from db
|
||||
*/
|
||||
|
||||
export const getNovelInfoFromDb = (novelUrl) => {
|
||||
return new Promise((resolve, reject) =>
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
`SELECT * FROM LibraryTable WHERE novelUrl=?`,
|
||||
[novelUrl],
|
||||
(txObj, { rows }) => {
|
||||
resolve(rows.item(0));
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get novel chapters from db
|
||||
*/
|
||||
|
||||
export const getChaptersFromDb = (novelUrl, filter, sort) => {
|
||||
return new Promise((resolve, reject) =>
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
`SELECT * FROM ChapterTable WHERE novelUrl=? ${filter} ${sort}`,
|
||||
[novelUrl],
|
||||
(txObj, { rows: { _array } }) => {
|
||||
resolve(_array);
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if novel is cached
|
||||
*/
|
||||
|
||||
export const checkNovelInDb = (novelUrl) => {
|
||||
return new Promise((resolve, reject) =>
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
"SELECT * FROM LibraryTable WHERE novelUrl=? LIMIT 1",
|
||||
[novelUrl],
|
||||
(txObj, res) => {
|
||||
if (res.rows.length !== 0) {
|
||||
resolve(true);
|
||||
} else {
|
||||
resolve(false);
|
||||
}
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updte library status of novel
|
||||
*/
|
||||
|
||||
export const toggleFavourite = async (libraryStatus, novelUrl) => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
"UPDATE LibraryTable SET libraryStatus = ? WHERE novelUrl=?",
|
||||
[!libraryStatus, novelUrl],
|
||||
(tx, res) => {},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch chapter from source and download or delete from db
|
||||
*/
|
||||
|
||||
export const downloadOrDeleteChapter = async (
|
||||
downloadStatus,
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapterUrl
|
||||
) => {
|
||||
if (downloadStatus === 0) {
|
||||
const response = await fetch(
|
||||
`https://lnreader-extensions.herokuapp.com/api/${extensionId}/novel/${novelUrl}${chapterUrl}`
|
||||
);
|
||||
|
||||
const chapter = await response.json();
|
||||
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
`UPDATE ChapterTable SET downloaded = 1 WHERE chapterUrl = ? AND novelUrl = ?`,
|
||||
[chapterUrl, novelUrl]
|
||||
);
|
||||
tx.executeSql(
|
||||
`INSERT INTO DownloadsTable (chapterUrl, novelUrl, chapterName, chapterText, prevChapter, nextChapter) VALUES (?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
chapterUrl,
|
||||
novelUrl,
|
||||
chapter.chapterName,
|
||||
chapter.chapterText,
|
||||
chapter.prevChapter,
|
||||
chapter.nextChapter,
|
||||
],
|
||||
(tx, res) => console.log(`Downloaded Chapter ${chapterUrl}`),
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
} else {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
`UPDATE ChapterTable SET downloaded = 0 WHERE chapterUrl = ? AND novelUrl= ?`,
|
||||
[chapterUrl, novelUrl]
|
||||
);
|
||||
tx.executeSql(
|
||||
`DELETE FROM DownloadsTable WHERE chapterUrl = ? AND novelUrl = ?`,
|
||||
[chapterUrl, novelUrl],
|
||||
(tx, res) => console.log(`Chapter deleted`),
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get reading history
|
||||
*/
|
||||
|
||||
export const getHistoryFromDb = async () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
"SELECT HistoryTable.chapterUrl, HistoryTable.historyId, HistoryTable.chapterName, HistoryTable.lastRead, LibraryTable.novelName, LibraryTable.libraryStatus, LibraryTable.novelCover, LibraryTable.novelUrl, LibraryTable.extensionId, LibraryTable.libraryStatus FROM HistoryTable INNER JOIN LibraryTable ON HistoryTable.novelUrl = LibraryTable.novelUrl ORDER BY HistoryTable.lastRead DESC",
|
||||
null,
|
||||
(txObj, { rows: { _array } }) => {
|
||||
resolve(_array);
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteChapterHistory = async (novelId) => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql("DELETE FROM HistoryTable WHERE novelUrl = ?", [novelId]);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get novels from library
|
||||
*/
|
||||
|
||||
export const getLibraryFromDb = async () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
"SELECT * FROM LibraryTable WHERE libraryStatus=1",
|
||||
null,
|
||||
(txObj, { rows: { _array } }) => {
|
||||
resolve(_array);
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Search novel in library
|
||||
*/
|
||||
|
||||
export const searchInLibrary = async (searchText) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
`SELECT * FROM LibraryTable WHERE libraryStatus=1 AND novelName LIKE '%${searchText}%'`,
|
||||
null,
|
||||
(txObj, { rows: { _array } }) => {
|
||||
resolve(_array);
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteHistory = () => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql("DELETE FROM HistoryTable; VACCUM;");
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteNovelsNotInLibrary = () => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
"DELETE FROM LibraryTable WHERE libraryStatus = 0",
|
||||
null,
|
||||
(txObj, res) => {
|
||||
console.log("Deleted");
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete entire database
|
||||
*/
|
||||
export const deleteDatabase = () => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
"DROP TABLE LibraryTable",
|
||||
null,
|
||||
(txObj, { rows: { _array } }) => {
|
||||
console.log("DELETED LIB TABLE");
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
tx.executeSql(
|
||||
"DROP TABLE ChapterTable",
|
||||
null,
|
||||
(txObj, { rows: { _array } }) => {
|
||||
console.log("DELETED CHAP TABLE");
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
tx.executeSql(
|
||||
"DROP TABLE HistoryTable",
|
||||
null,
|
||||
(txObj, { rows: { _array } }) => {
|
||||
console.log("DELETED History TABLE");
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
tx.executeSql(
|
||||
"DROP TABLE DownloadsTable",
|
||||
null,
|
||||
(txObj, { rows: { _array } }) => {
|
||||
console.log("DELETED Downloads TABLE");
|
||||
ToastAndroid.show("Database deleted", ToastAndroid.SHORT);
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const downloadChapterFromSource = async (
|
||||
extensionId,
|
||||
novelUrl,
|
||||
chapterUrl
|
||||
) => {
|
||||
const downloadUrl = `https://lnreader-extensions.herokuapp.com/api/${extensionId}/novel/${novelUrl}${chapterUrl}`;
|
||||
|
||||
const response = await fetch(downloadUrl);
|
||||
const chapter = await response.json();
|
||||
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
`UPDATE ChapterTable SET downloaded = 1 WHERE chapterUrl = ? AND novelUrl = ?`,
|
||||
[chapterUrl, novelUrl]
|
||||
);
|
||||
tx.executeSql(
|
||||
`INSERT INTO DownloadsTable (chapterUrl, novelUrl, chapterName, chapterText, prevChapter, nextChapter) VALUES (?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
chapterUrl,
|
||||
novelUrl,
|
||||
chapter.chapterName,
|
||||
chapter.chapterText,
|
||||
chapter.prevChapter,
|
||||
chapter.nextChapter,
|
||||
],
|
||||
(tx, res) => console.log(`Downloaded Chapter ${chapterUrl}`),
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteChapterFromDb = (extensionId, novelUrl, chapterUrl) => {
|
||||
const updateIsDownloadedQuery = `UPDATE ChapterTable SET downloaded = 0 WHERE chapterUrl = ? AND novelUrl = ? AND extensionId = ?`;
|
||||
const deleteChapterQuery = `DELETE FROM DownloadsTable WHERE chapterUrl = ? AND novelUrl = ? AND extensionId = ?`;
|
||||
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(updateIsDownloadedQuery, [
|
||||
chapterUrl,
|
||||
novelUrl,
|
||||
extensionId,
|
||||
]);
|
||||
tx.executeSql(
|
||||
deleteChapterQuery,
|
||||
[chapterUrl, novelUrl, extensionId],
|
||||
(tx, res) => console.log(`Chapter deleted`),
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const insertIntoHistory = async (chapterUrl, chapterName, novelUrl) => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
"INSERT OR REPLACE INTO HistoryTable (chapterUrl, novelUrl, chapterName, lastRead) VALUES ( ?, ?, ?, (datetime('now','localtime')))",
|
||||
[chapterUrl, novelUrl, chapterName]
|
||||
);
|
||||
tx.executeSql(
|
||||
"UPDATE LibraryTable SET lastRead = ?, lastReadName = ?, unread = 0 WHERE novelUrl = ?",
|
||||
[chapterUrl, chapterName, novelUrl]
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const chapterRead = async (chapterUrl, novelUrl) => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
"UPDATE ChapterTable SET `read` = 1 WHERE chapterUrl = ? AND novelUrl = ?",
|
||||
[chapterUrl, novelUrl],
|
||||
(tx, res) => {},
|
||||
(tx, error) => console.log(error)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const isChapterDownloaded = async (chapterUrl, novelUrl) => {
|
||||
return new Promise((resolve, reject) =>
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
"SELECT * FROM DownloadsTable WHERE chapterUrl=? AND novelUrl=?",
|
||||
[chapterUrl, novelUrl],
|
||||
(txObj, res) => {
|
||||
if (res.rows.length !== 0) {
|
||||
resolve(true);
|
||||
} else {
|
||||
resolve(false);
|
||||
}
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const getChapterFromDb = async (chapterUrl, novelUrl) => {
|
||||
return new Promise((resolve, reject) =>
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
`SELECT * FROM DownloadsTable WHERE chapterUrl = ? AND novelUrl = ?`,
|
||||
[chapterUrl, novelUrl],
|
||||
(tx, results) => {
|
||||
resolve(results.rows.item(0));
|
||||
},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
import * as SQLite from "expo-sqlite";
|
||||
import { fetchNovel } from "../source/Source";
|
||||
const db = SQLite.openDatabase("lnreader.db");
|
||||
|
||||
const updateNovelDetails = async (novel, novelId) => {
|
||||
db.transaction((tx) => {
|
||||
tx.executeSql(
|
||||
"UPDATE novels SET novelName = ?, novelCover = ?, novelSummary = ?, author = ?, artist = ?, genre = ?, status = ? WHERE novelId = ?",
|
||||
[
|
||||
novel.novelName,
|
||||
novel.novelCover,
|
||||
novel.novelSummary,
|
||||
novel.author,
|
||||
novel.artist,
|
||||
novel.genre,
|
||||
novel.status,
|
||||
novelId,
|
||||
],
|
||||
(txObj, res) => {},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const updateNovel = async (extensionId, novelUrl, novelId) => {
|
||||
let novel = await fetchNovel(extensionId, novelUrl);
|
||||
// console.log(novel);
|
||||
await updateNovelDetails(novel, novelId);
|
||||
|
||||
db.transaction((tx) => {
|
||||
novel.chapters.map((chapter) =>
|
||||
tx.executeSql(
|
||||
"INSERT OR IGNORE INTO chapters (chapterUrl, chapterName, releaseDate, novelId) values (?, ?, ?, ?)",
|
||||
[
|
||||
chapter.chapterUrl,
|
||||
chapter.chapterName,
|
||||
chapter.releaseDate,
|
||||
novelId,
|
||||
],
|
||||
(txObj, res) => {},
|
||||
(txObj, error) => console.log("Error ", error)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
return novel;
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
export const fetchSources = async () => {
|
||||
const url = `https://lnreader-extensions.herokuapp.com/api/`;
|
||||
|
||||
let res = await fetch(url);
|
||||
let sources = await res.json();
|
||||
|
||||
return sources;
|
||||
};
|
||||
|
||||
export const fetchNovel = async (sourceId, novelUrl) => {
|
||||
const url = `https://lnreader-extensions.herokuapp.com/api/${sourceId}/novel/${novelUrl}`;
|
||||
|
||||
let data = await fetch(url);
|
||||
let res = await data.json();
|
||||
|
||||
const novel = {
|
||||
novelUrl: res.novelUrl,
|
||||
sourceUrl: res.sourceUrl,
|
||||
source: res.sourceName,
|
||||
sourceId: res.extensionId,
|
||||
novelName: res.novelName,
|
||||
novelCover: res.novelCover,
|
||||
novelSummary: res.novelSummary,
|
||||
author: res["Author(s)"],
|
||||
artist: res["Artist(s)"],
|
||||
status: res.Status,
|
||||
genre: res["Genre(s)"],
|
||||
followed: 0,
|
||||
chapters: res.novelChapters,
|
||||
};
|
||||
|
||||
return novel;
|
||||
};
|
||||
|
||||
export const fetchChapter = async (sourceId, novelUrl, chapterUrl) => {
|
||||
const url = `https://lnreader-extensions.herokuapp.com/api/${sourceId}/novel/${novelUrl}${chapterUrl}`;
|
||||
|
||||
let res = await fetch(url);
|
||||
let chapter = await res.json();
|
||||
|
||||
return chapter;
|
||||
};
|
||||
+1
-1
@@ -21,7 +21,7 @@ export const lightTheme = {
|
||||
textColorPrimary: "#000000",
|
||||
textColorSecondary: "rgba(0,0,0,0.54)",
|
||||
textColorHintDark: "rgba(0,0,0,0.38)",
|
||||
rippleColor: "#C2C2C2",
|
||||
rippleColor: "rgba(0,0,0,0.12)",
|
||||
searchBarColor: "#FFFFFF",
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user