diff --git a/server/src/main/kotlin/ir/armor/tachidesk/Main.kt b/server/src/main/kotlin/ir/armor/tachidesk/Main.kt index f6f4dbf..fbc29c4 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/Main.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/Main.kt @@ -8,6 +8,7 @@ import ir.armor.tachidesk.util.getExtensionList import ir.armor.tachidesk.util.getManga import ir.armor.tachidesk.util.getMangaList import ir.armor.tachidesk.util.getPages +import ir.armor.tachidesk.util.getSource import ir.armor.tachidesk.util.getSourceList import ir.armor.tachidesk.util.installAPK import ir.armor.tachidesk.util.sourceFilters @@ -33,6 +34,10 @@ class Main { @JvmStatic fun main(args: Array) { + System.getProperties()["proxySet"] = "true" + System.getProperties()["socksProxyHost"] = "127.0.0.1" + System.getProperties()["socksProxyPort"] = "2020" + // make sure everything we need exists applicationSetup() @@ -75,6 +80,11 @@ class Main { ctx.json(getSourceList()) } + app.get("/api/v1/source/:sourceId") { ctx -> + val sourceId = ctx.pathParam("sourceId").toLong() + ctx.json(getSource(sourceId)) + } + app.get("/api/v1/source/:sourceId/popular/:pageNum") { ctx -> val sourceId = ctx.pathParam("sourceId").toLong() val pageNum = ctx.pathParam("pageNum").toInt() diff --git a/server/src/main/kotlin/ir/armor/tachidesk/util/Chapter.kt b/server/src/main/kotlin/ir/armor/tachidesk/util/Chapter.kt index 65eb009..aeb556a 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/util/Chapter.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/util/Chapter.kt @@ -53,7 +53,7 @@ fun getChapterList(mangaId: Int): List { } } -fun getPages(chapterId: Int, mangaId: Int): List { +fun getPages(chapterId: Int, mangaId: Int): Pair> { return transaction { val chapterEntry = ChapterTable.select { ChapterTable.id eq chapterId }.firstOrNull()!! assert(mangaId == chapterEntry[ChapterTable.manga].value) // sanity check @@ -67,12 +67,24 @@ fun getPages(chapterId: Int, mangaId: Int): List { } ).toBlocking().first() - return@transaction pagesList.map { + val chapter = ChapterDataClass( + chapterEntry[ChapterTable.id].value, + chapterEntry[ChapterTable.url], + chapterEntry[ChapterTable.name], + chapterEntry[ChapterTable.date_upload], + chapterEntry[ChapterTable.chapter_number], + chapterEntry[ChapterTable.scanlator], + mangaId + ) + + val pages = pagesList.map { PageDataClass( it.index, getTrueImageUrl(it, source) ) } + + return@transaction Pair(chapter, pages) } } diff --git a/server/src/main/kotlin/ir/armor/tachidesk/util/MangaList.kt b/server/src/main/kotlin/ir/armor/tachidesk/util/MangaList.kt index f7f072e..1ec874d 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/util/MangaList.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/util/MangaList.kt @@ -40,7 +40,7 @@ fun getMangaList(sourceId: Long, pageNum: Int = 1, popular: Boolean): List { } } } + +fun getSource(sourceId: Long): SourceDataClass { + return transaction { + val source = SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()!! + + return@transaction SourceDataClass( + source[SourceTable.id].value.toString(), + source[SourceTable.name], + Locale(source[SourceTable.lang]).getDisplayLanguage(Locale(source[SourceTable.lang])), + ExtensionsTable.select { ExtensionsTable.id eq source[SourceTable.extension] }.first()[ExtensionsTable.iconUrl], + getHttpSource(source[SourceTable.id].value).supportsLatest + ) + } +} diff --git a/webUI/react/src/App.tsx b/webUI/react/src/App.tsx index 21d6b3b..ffe4465 100644 --- a/webUI/react/src/App.tsx +++ b/webUI/react/src/App.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { BrowserRouter as Router, Route, Switch, } from 'react-router-dom'; @@ -10,38 +10,44 @@ import MangaList from './screens/MangaList'; import Manga from './screens/Manga'; import Reader from './screens/Reader'; import Search from './screens/Search'; +import NavBarTitle from './context/NavbarTitle'; export default function App() { + const [title, setTitle] = useState('Tachidesk'); + const contextValue = { title, setTitle }; + return ( - + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + ); } diff --git a/webUI/react/src/components/NavBar.tsx b/webUI/react/src/components/NavBar.tsx index 16820f8..894bdd2 100644 --- a/webUI/react/src/components/NavBar.tsx +++ b/webUI/react/src/components/NavBar.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useContext, useState } from 'react'; import { makeStyles } from '@material-ui/core/styles'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; @@ -6,6 +6,7 @@ import Typography from '@material-ui/core/Typography'; import IconButton from '@material-ui/core/IconButton'; import MenuIcon from '@material-ui/icons/Menu'; import TemporaryDrawer from './TemporaryDrawer'; +import NavBarTitle from '../context/NavbarTitle'; const useStyles = makeStyles((theme) => ({ root: { @@ -22,6 +23,7 @@ const useStyles = makeStyles((theme) => ({ export default function NavBar() { const classes = useStyles(); const [drawerOpen, setDrawerOpen] = useState(false); + const { title } = useContext(NavBarTitle); return (
@@ -38,7 +40,7 @@ export default function NavBar() { - Tachidesk + {title} diff --git a/webUI/react/src/context/NavbarTitle.tsx b/webUI/react/src/context/NavbarTitle.tsx new file mode 100644 index 0000000..9e2a13d --- /dev/null +++ b/webUI/react/src/context/NavbarTitle.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +type ContextType = { + title: string + setTitle: React.Dispatch> +}; + +const NavBarTitle = React.createContext({ + title: 'Tachidesk', + setTitle: ():void => {}, +}); + +export default NavBarTitle; diff --git a/webUI/react/src/screens/Extensions.tsx b/webUI/react/src/screens/Extensions.tsx index 51622fd..8678e05 100644 --- a/webUI/react/src/screens/Extensions.tsx +++ b/webUI/react/src/screens/Extensions.tsx @@ -1,9 +1,12 @@ -import React, { useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import ExtensionCard from '../components/ExtensionCard'; +import NavBarTitle from '../context/NavbarTitle'; export default function Extensions() { - let mapped; + const { setTitle } = useContext(NavBarTitle); + setTitle('Extensions'); const [extensions, setExtensions] = useState([]); + let mapped; useEffect(() => { fetch('http://127.0.0.1:4567/api/v1/extension/list') diff --git a/webUI/react/src/screens/Manga.tsx b/webUI/react/src/screens/Manga.tsx index 52ed6cb..8f74ff7 100644 --- a/webUI/react/src/screens/Manga.tsx +++ b/webUI/react/src/screens/Manga.tsx @@ -1,10 +1,12 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useContext } from 'react'; import { useParams } from 'react-router-dom'; import ChapterCard from '../components/ChapterCard'; import MangaDetails from '../components/MangaDetails'; +import NavBarTitle from '../context/NavbarTitle'; export default function Manga() { const { id } = useParams<{id: string}>(); + const { setTitle } = useContext(NavBarTitle); const [manga, setManga] = useState(); const [chapters, setChapters] = useState([]); @@ -12,7 +14,10 @@ export default function Manga() { useEffect(() => { fetch(`http://127.0.0.1:4567/api/v1/manga/${id}/`) .then((response) => response.json()) - .then((data) => setManga(data)); + .then((data: IManga) => { + setManga(data); + setTitle(data.title); + }); }, []); useEffect(() => { diff --git a/webUI/react/src/screens/MangaList.tsx b/webUI/react/src/screens/MangaList.tsx index 93ba238..4a547c1 100644 --- a/webUI/react/src/screens/MangaList.tsx +++ b/webUI/react/src/screens/MangaList.tsx @@ -1,12 +1,20 @@ -import React, { useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import MangaGrid from '../components/MangaGrid'; +import NavBarTitle from '../context/NavbarTitle'; export default function MangaList(props: { popular: boolean }) { const { sourceId } = useParams<{sourceId: string}>(); + const { setTitle } = useContext(NavBarTitle); const [mangas, setMangas] = useState([]); const [lastPageNum] = useState(1); + useEffect(() => { + fetch(`http://127.0.0.1:4567/api/v1/source/${sourceId}`) + .then((response) => response.json()) + .then((data: { name: string }) => setTitle(data.name)); + }, []); + useEffect(() => { const sourceType = props.popular ? 'popular' : 'latest'; fetch(`http://127.0.0.1:4567/api/v1/source/${sourceId}/${sourceType}/${lastPageNum}`) diff --git a/webUI/react/src/screens/Reader.tsx b/webUI/react/src/screens/Reader.tsx index 1a4b3b4..90e4bd8 100644 --- a/webUI/react/src/screens/Reader.tsx +++ b/webUI/react/src/screens/Reader.tsx @@ -1,5 +1,6 @@ -import React, { useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; +import NavBarTitle from '../context/NavbarTitle'; const style = { display: 'flex', @@ -14,14 +15,24 @@ interface IPage { imageUrl: string } +interface IData { + first: IChapter + second: IPage[] +} + export default function Reader() { + const { setTitle } = useContext(NavBarTitle); + const [pages, setPages] = useState([]); const { chapterId, mangaId } = useParams<{chapterId: string, mangaId: string}>(); useEffect(() => { fetch(`http://127.0.0.1:4567/api/v1/manga/${mangaId}/chapter/${chapterId}`) .then((response) => response.json()) - .then((data) => setPages(data)); + .then((data:IData) => { + setTitle(data.first.name); + setPages(data.second); + }); }, []); pages.sort((a, b) => (a.index - b.index)); diff --git a/webUI/react/src/screens/Search.tsx b/webUI/react/src/screens/Search.tsx index b79f977..e40c581 100644 --- a/webUI/react/src/screens/Search.tsx +++ b/webUI/react/src/screens/Search.tsx @@ -1,8 +1,9 @@ -import React, { useState } from 'react'; +import React, { useContext, useState } from 'react'; import { makeStyles } from '@material-ui/core/styles'; import TextField from '@material-ui/core/TextField'; import Button from '@material-ui/core/Button'; import MangaGrid from '../components/MangaGrid'; +import NavBarTitle from '../context/NavbarTitle'; const useStyles = makeStyles((theme) => ({ root: { @@ -14,6 +15,8 @@ const useStyles = makeStyles((theme) => ({ })); export default function Search() { + const { setTitle } = useContext(NavBarTitle); + setTitle('Search'); const classes = useStyles(); const [error, setError] = useState(false); const [mangas, setMangas] = useState([]); diff --git a/webUI/react/src/screens/Sources.tsx b/webUI/react/src/screens/Sources.tsx index 96d2cd5..26dadc9 100644 --- a/webUI/react/src/screens/Sources.tsx +++ b/webUI/react/src/screens/Sources.tsx @@ -1,9 +1,12 @@ -import React, { useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import SourceCard from '../components/SourceCard'; +import NavBarTitle from '../context/NavbarTitle'; export default function Sources() { - let mapped; + const { setTitle } = useContext(NavBarTitle); + setTitle('Sources'); const [sources, setSources] = useState([]); + let mapped; useEffect(() => { fetch('http://127.0.0.1:4567/api/v1/source/list')