Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d9f5b0bd9 | |||
| 5cbb2a0481 | |||
| a3b17365b7 | |||
| 2847554b8f | |||
| fb166eadd4 | |||
| 046c11f785 | |||
| eac7436b18 | |||
| 1a23804e51 | |||
| 08be142858 | |||
| f421dbfe69 | |||
| 0d7ad65dbe | |||
| 9e946406bc | |||
| 9142d46fae | |||
| 39ed134f96 | |||
| 7e4b495398 | |||
| c12242b760 | |||
| 8b2fd28a54 | |||
| 466f21a7e8 | |||
| 26a7e2f1cd | |||
| c155d78d52 | |||
| 687dad5fc0 | |||
| 33c4cdbc48 | |||
| e46cb9738f | |||
| aef37fcc9e | |||
| ac51f40a91 | |||
| cd82af2d76 | |||
| 790040cd68 | |||
| 95465ec265 | |||
| 5313d91bf2 | |||
| 63783984c6 | |||
| 97b9b1b6c9 | |||
| 34a7c24e0b | |||
| 12765a771f | |||
| 39850c71b0 | |||
| 9e43645a67 | |||
| 7dc7f4d905 | |||
| c537c1bf29 | |||
| 5f3ddbd1b2 | |||
| f297e3790c | |||
| ef3532357f | |||
| 48f29edf6c |
@@ -0,0 +1,7 @@
|
|||||||
|
org.gradle.daemon=false
|
||||||
|
org.gradle.jvmargs=-Xmx5120m
|
||||||
|
org.gradle.workers.max=5
|
||||||
|
org.gradle.parallel=true
|
||||||
|
|
||||||
|
kotlin.incremental=false
|
||||||
|
kotlin.compiler.execution.strategy=in-process
|
||||||
Executable
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cp ../master/repo/* .
|
||||||
|
new_build=$(ls | tail -1)
|
||||||
|
echo "New build file name: $new_build"
|
||||||
|
|
||||||
|
cp -f $new_build Tachidesk-latest.jar
|
||||||
|
|
||||||
|
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git config --global user.name "github-actions[bot]"
|
||||||
|
git status
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
git add .
|
||||||
|
git commit -m "Update repo"
|
||||||
|
git push
|
||||||
|
else
|
||||||
|
echo "No changes to commit"
|
||||||
|
fi
|
||||||
Executable
+14
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
|
||||||
|
mkdir -p repo/
|
||||||
|
|
||||||
|
# Get last commit message
|
||||||
|
last_commit_log=$(git log -1 --pretty=format:"%s")
|
||||||
|
echo "last commit log: $last_commit_log"
|
||||||
|
|
||||||
|
filter_count=$(echo "$last_commit_log" | grep -c "[RELEASE CI]" )
|
||||||
|
|
||||||
|
if [ "$filter_count" -gt 0 ]; then
|
||||||
|
cp server/build/Tachidesk-*.jar repo/
|
||||||
|
fi
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check_wrapper:
|
||||||
|
name: Validate Gradle Wrapper
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Clone repo
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Validate Gradle Wrapper
|
||||||
|
uses: gradle/wrapper-validation-action@v1
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build FatJar
|
||||||
|
needs: check_wrapper
|
||||||
|
if: "!startsWith(github.event.head_commit.message, '[SKIP CI]')"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Cancel previous runs
|
||||||
|
uses: styfle/cancel-workflow-action@0.5.0
|
||||||
|
with:
|
||||||
|
access_token: ${{ github.token }}
|
||||||
|
|
||||||
|
- name: Checkout master branch
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
ref: master
|
||||||
|
path: master
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up JDK 1.8
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 1.8
|
||||||
|
|
||||||
|
- name: Copy CI gradle.properties
|
||||||
|
run: |
|
||||||
|
cd master
|
||||||
|
mkdir -p ~/.gradle
|
||||||
|
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
|
||||||
|
|
||||||
|
- name: Download and process android.jar
|
||||||
|
if: github.event_name == 'push' && github.repository == 'AriaMoradi/Tachidesk'
|
||||||
|
run: |
|
||||||
|
cd master
|
||||||
|
./scripts/getAndroid.sh
|
||||||
|
|
||||||
|
- name: Build the Jar
|
||||||
|
uses: eskatos/gradle-command-action@v1
|
||||||
|
with:
|
||||||
|
build-root-directory: master
|
||||||
|
wrapper-directory: master
|
||||||
|
arguments: :server:shadowJar --stacktrace
|
||||||
|
wrapper-cache-enabled: true
|
||||||
|
dependencies-cache-enabled: true
|
||||||
|
configuration-cache-enabled: true
|
||||||
|
|
||||||
|
- name: Create repo artifacts
|
||||||
|
if: github.event_name == 'push' && github.repository == 'AriaMoradi/Tachidesk'
|
||||||
|
run: |
|
||||||
|
cd master
|
||||||
|
./.github/scripts/create-repo.sh
|
||||||
|
|
||||||
|
- name: Checkout repo branch
|
||||||
|
if: github.event_name == 'push' && github.repository == 'AriaMoradi/Tachidesk'
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
ref: repo
|
||||||
|
path: repo
|
||||||
|
|
||||||
|
- name: Deploy repo
|
||||||
|
if: github.event_name == 'push' && github.repository == 'AriaMoradi/Tachidesk'
|
||||||
|
run: |
|
||||||
|
cd repo
|
||||||
|
../master/.github/scripts/commit-repo.sh
|
||||||
@@ -1,33 +1,50 @@
|
|||||||
# Tachidesk
|
# Tachidesk
|
||||||
A not so much port of [Tachiyomi](https://tachiyomi.org/) to the web (and later Electron for the desktop experience)!
|
A free and open source manga reader than runs extensions built for [Tachiyomi](https://tachiyomi.org/) which runs on desktop operating systems.
|
||||||
|
|
||||||
|
Ability to read and write Tachiyomi compatible backups and syncing is a planned feature.
|
||||||
|
|
||||||
|
## How does it work?
|
||||||
This project has two components:
|
This project has two components:
|
||||||
1. **server:** contains some of the original Tachiyomi code and serves a REST API
|
1. **server:** contains the implementation of [tachiyomi's extensions library](https://github.com/tachiyomiorg/extensions-lib) and uses an Android compatibility library to run apk extensions. All this concludes to serving a REST API to `webUI`.
|
||||||
2. **webUI:** A react project that works with the server to do the presentation
|
2. **webUI:** A react SPA project that works with the server to do the presentation.
|
||||||
|
|
||||||
## How do I run the thing?
|
## How do I run the thing?
|
||||||
### Get Android stubs jar(do this only once)
|
#### Running pre-built jar packages
|
||||||
|
Download the latest (or a working more stable) release from [the repo branch](https://github.com/AriaMoradi/Tachidesk/tree/repo) or obtain it from [the releases section](https://github.com/AriaMoradi/Tachidesk/releases).
|
||||||
|
|
||||||
|
Double click on the jar file or run `java -jar Tachidesk-latest.jar` or `java -jar Tachidesk-vX.Y.Z-rxxx.jar`
|
||||||
|
|
||||||
|
The server will be running on `http://localhost:4567` open this url in your browser.
|
||||||
|
|
||||||
|
## Building from source
|
||||||
|
### Get Android stubs jar
|
||||||
#### Manual download
|
#### Manual download
|
||||||
Download [android.jar](https://raw.githubusercontent.com/AriaMoradi/Tachidesk/android-jar/android.jar) and put it under `AndroidCompat/lib`.
|
Download [android.jar](https://raw.githubusercontent.com/AriaMoradi/Tachidesk/android-jar/android.jar) and put it under `AndroidCompat/lib`.
|
||||||
#### Building from source(needs `bash`, `curl`, `base64`, `zip` to work)
|
#### Building from source(needs `bash`, `curl`, `base64`, `zip` to work)
|
||||||
run `scripts/getAndroid.sh` from project's root directory to download and rebuild the jar file from Google's repository.
|
Run `scripts/getAndroid.sh` from project's root directory to download and rebuild the jar file from Google's repository.
|
||||||
### building the jar
|
### building the jar
|
||||||
run `./gradlew :server:fatJar` the resulting jar file will be `server/build/server-1.0-all.jar`. Simply double click on it or run `java -jar server-1.0-all.jar`. The server will be running on `http://localhost:4567` open this url in your browser.
|
Run `./gradlew shadowJar` the resulting built jar file will be `server/build/Tachidesk-vX.Y.Z-rxxx.jar`.
|
||||||
## running for development purposes
|
## Running for development purposes
|
||||||
### The Server
|
### `server` module
|
||||||
run `./gradlew :server:run -x :webUI:yarn_build --stacktrace` to run the server
|
Run `./gradlew :server:run -x :webUI:copyBuild --stacktrace` to run the server
|
||||||
### the webUI
|
### `webUI` module
|
||||||
how to do it is described in `webUI/react/README.md` but for short,
|
How to do it is described in `webUI/react/README.md` but for short,
|
||||||
first cd into `webUI/react` then run `yarn` to install the node modules(do this only once)
|
first cd into `webUI/react` then run `yarn` to install the node modules(do this only once)
|
||||||
then `yarn start` to start the client if a new browser window doesn't start automatically,
|
then `yarn start` to start the client if a new browser window doesn't start automatically,
|
||||||
then open `http://127.0.0.1:3000` in a modern browser.
|
then open `http://127.0.0.1:3000` in a modern browser. This is a `create-react-app` project
|
||||||
|
and supports HMR and all the other goodies you'll need.
|
||||||
|
|
||||||
## Is the application usable? Should I test it?
|
## Is this application usable? Should I test it?
|
||||||
Checkout [the state of project](https://github.com/AriaMoradi/Tachidesk/issues/2) to see what's implemented.
|
Checkout [the state of project](https://github.com/AriaMoradi/Tachidesk/issues/2) to see what's implemented.
|
||||||
|
|
||||||
|
## Credit
|
||||||
|
The `AndroidCompat` module and `scripts/getAndroid.sh` was originally developed by [@null-dev](https://github.com/null-dev) for [TachiWeb-Server](https://github.com/Tachiweb/TachiWeb-server) and is licensed under `Apache License Version 2.0`.
|
||||||
|
|
||||||
|
Parts of [tachiyomi](https://github.com/tachiyomiorg/tachiyomi) is adopted into this codebase, also licensed under `Apache License Version 2.0`.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright (C) 2020 Aria Moradi
|
Copyright (C) 2020-2021 Aria Moradi and contributors
|
||||||
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
|||||||
@@ -58,9 +58,8 @@ function dedup() {
|
|||||||
|
|
||||||
pushd ..
|
pushd ..
|
||||||
dedup AndroidCompat/src/main/java
|
dedup AndroidCompat/src/main/java
|
||||||
dedup TachiServer/src/main/java
|
dedup server/src/main/java
|
||||||
dedup Tachiyomi-App/src/main/java
|
dedup server/src/main/kotlin
|
||||||
dedup Tachiyomi-App/src/compat/java
|
|
||||||
popd
|
popd
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|||||||
+19
-1
@@ -1,4 +1,5 @@
|
|||||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||||
|
import java.io.BufferedReader
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
// id("org.jetbrains.kotlin.jvm") version "1.4.21"
|
// id("org.jetbrains.kotlin.jvm") version "1.4.21"
|
||||||
@@ -6,6 +7,8 @@ plugins {
|
|||||||
id("com.github.johnrengelman.shadow") version "6.1.0"
|
id("com.github.johnrengelman.shadow") version "6.1.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val TachideskVersion = "v0.0.2"
|
||||||
|
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@@ -102,6 +105,19 @@ sourceSets {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val TachideskRevision = Runtime
|
||||||
|
.getRuntime()
|
||||||
|
.exec("git rev-list master --count")
|
||||||
|
.let { process ->
|
||||||
|
process.waitFor()
|
||||||
|
val output = process.inputStream.use {
|
||||||
|
it.bufferedReader().use(BufferedReader::readText)
|
||||||
|
}
|
||||||
|
process.destroy()
|
||||||
|
"r"+output.trim()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
jar {
|
jar {
|
||||||
manifest {
|
manifest {
|
||||||
@@ -115,12 +131,14 @@ tasks {
|
|||||||
}
|
}
|
||||||
shadowJar {
|
shadowJar {
|
||||||
manifest.inheritFrom(jar.get().manifest) //will make your shadowJar (produced by jar task) runnable
|
manifest.inheritFrom(jar.get().manifest) //will make your shadowJar (produced by jar task) runnable
|
||||||
|
archiveBaseName.set("Tachidesk")
|
||||||
|
archiveVersion.set(TachideskVersion)
|
||||||
|
archiveClassifier.set(TachideskRevision)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<ShadowJar> {
|
tasks.withType<ShadowJar> {
|
||||||
destinationDir = File("$rootDir/server/build")
|
destinationDir = File("$rootDir/server/build")
|
||||||
//dependsOn(":webUI:copyBuild")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named("processResources") {
|
tasks.named("processResources") {
|
||||||
|
|||||||
@@ -36,10 +36,14 @@ class Main {
|
|||||||
androidCompat.startApp(App())
|
androidCompat.startApp(App())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val app = Javalin.create { config ->
|
val app = Javalin.create { config ->
|
||||||
// config.addSinglePageRoot("/", "")
|
try {
|
||||||
config.addStaticFiles("/react")
|
this::class.java.classLoader.getResource("/react/index.html")
|
||||||
|
config.addStaticFiles("/react")
|
||||||
|
config.addSinglePageRoot("/","/react/index.html")
|
||||||
|
} catch (e: RuntimeException) {
|
||||||
|
println("Warning: react build files are missing.")
|
||||||
|
}
|
||||||
}.start(4567)
|
}.start(4567)
|
||||||
|
|
||||||
|
|
||||||
@@ -68,12 +72,12 @@ class Main {
|
|||||||
app.get("/api/v1/source/:sourceId/popular/:pageNum") { ctx ->
|
app.get("/api/v1/source/:sourceId/popular/:pageNum") { ctx ->
|
||||||
val sourceId = ctx.pathParam("sourceId").toLong()
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
||||||
val pageNum = ctx.pathParam("pageNum").toInt()
|
val pageNum = ctx.pathParam("pageNum").toInt()
|
||||||
ctx.json(getMangaList(sourceId,pageNum,popular = true))
|
ctx.json(getMangaList(sourceId, pageNum, popular = true))
|
||||||
}
|
}
|
||||||
app.get("/api/v1/source/:sourceId/latest/:pageNum") { ctx ->
|
app.get("/api/v1/source/:sourceId/latest/:pageNum") { ctx ->
|
||||||
val sourceId = ctx.pathParam("sourceId").toLong()
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
||||||
val pageNum = ctx.pathParam("pageNum").toInt()
|
val pageNum = ctx.pathParam("pageNum").toInt()
|
||||||
ctx.json(getMangaList(sourceId,pageNum,popular = false))
|
ctx.json(getMangaList(sourceId, pageNum, popular = false))
|
||||||
}
|
}
|
||||||
|
|
||||||
app.get("/api/v1/manga/:mangaId/") { ctx ->
|
app.get("/api/v1/manga/:mangaId/") { ctx ->
|
||||||
@@ -89,11 +93,32 @@ class Main {
|
|||||||
app.get("/api/v1/manga/:mangaId/chapter/:chapterId") { ctx ->
|
app.get("/api/v1/manga/:mangaId/chapter/:chapterId") { ctx ->
|
||||||
val chapterId = ctx.pathParam("chapterId").toInt()
|
val chapterId = ctx.pathParam("chapterId").toInt()
|
||||||
val mangaId = ctx.pathParam("mangaId").toInt()
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
||||||
ctx.json(getPages(chapterId,mangaId))
|
ctx.json(getPages(chapterId, mangaId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// global search
|
||||||
|
app.get("/api/v1/search/:searchTerm") { ctx ->
|
||||||
|
val searchTerm = ctx.pathParam("searchTerm")
|
||||||
|
ctx.json(sourceGlobalSearch(searchTerm))
|
||||||
|
}
|
||||||
|
|
||||||
|
// single source search
|
||||||
|
app.get("/api/v1/source/:sourceId/search/:searchTerm") { ctx ->
|
||||||
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
||||||
|
val searchTerm = ctx.pathParam("searchTerm")
|
||||||
|
ctx.json(sourceSearch(sourceId, searchTerm))
|
||||||
|
}
|
||||||
|
|
||||||
|
// source filter list
|
||||||
|
app.get("/api/v1/source/:sourceId/filters/") { ctx ->
|
||||||
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
||||||
|
ctx.json(sourceFilters(sourceId))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
|
||||||
|
fun sourceFilters(sourceId: Long) {
|
||||||
|
val source = getHttpSource(sourceId)
|
||||||
|
//source.getFilterList().toItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sourceSearch(sourceId: Long, searchTerm: String) {
|
||||||
|
val source = getHttpSource(sourceId)
|
||||||
|
//source.fetchSearchManga()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sourceGlobalSearch(searchTerm: String) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data class FilterWrapper(
|
||||||
|
val type: String,
|
||||||
|
val filter: Any
|
||||||
|
)
|
||||||
|
|
||||||
|
//private fun FilterList.toItems(): List<FilterWrapper> {
|
||||||
|
// return mapNotNull { filter ->
|
||||||
|
// when (filter) {
|
||||||
|
// is Filter.Header -> FilterWrapper("Header",filter)
|
||||||
|
// is Filter.Separator -> FilterWrapper("Separator",filter)
|
||||||
|
// is Filter.CheckBox -> FilterWrapper("CheckBox",filter)
|
||||||
|
// is Filter.TriState -> FilterWrapper("TriState",filter)
|
||||||
|
// is Filter.Text -> FilterWrapper("Text",filter)
|
||||||
|
// is Filter.Select<*> -> FilterWrapper("Select",filter)
|
||||||
|
// is Filter.Group<*> -> {
|
||||||
|
// val group = GroupItem(filter)
|
||||||
|
// val subItems = filter.state.mapNotNull {
|
||||||
|
// when (it) {
|
||||||
|
// is Filter.CheckBox -> FilterWrapper("CheckBox",filter)
|
||||||
|
// is Filter.TriState -> FilterWrapper("TriState",filter)
|
||||||
|
// is Filter.Text -> FilterWrapper("Text",filter)
|
||||||
|
// is Filter.Select<*> -> FilterWrapper("Select",filter)
|
||||||
|
// else -> null
|
||||||
|
// } as? ISectionable<*, *>
|
||||||
|
// }
|
||||||
|
// subItems.forEach { it.header = group }
|
||||||
|
// group.subItems = subItems
|
||||||
|
// group
|
||||||
|
// }
|
||||||
|
// is Filter.Sort -> {
|
||||||
|
// val group = SortGroup(filter)
|
||||||
|
// val subItems = filter.values.map {
|
||||||
|
// SortItem(it, group)
|
||||||
|
// }
|
||||||
|
// group.subItems = subItems
|
||||||
|
// group
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
@@ -4,11 +4,11 @@ plugins {
|
|||||||
|
|
||||||
node {
|
node {
|
||||||
workDir = file("${project.projectDir}/react/")
|
workDir = file("${project.projectDir}/react/")
|
||||||
nodeModulesDir = file("${project.projectDir}/react/node_modules")
|
nodeModulesDir = file("${project.projectDir}/react/")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named("yarn_build") {
|
tasks.named("yarn_build") {
|
||||||
dependsOn("yarn_install")
|
dependsOn("yarn") // install node_moduels
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register<Copy>("copyBuild") {
|
tasks.register<Copy>("copyBuild") {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import Extensions from './screens/Extensions';
|
|||||||
import MangaList from './screens/MangaList';
|
import MangaList from './screens/MangaList';
|
||||||
import Manga from './screens/Manga';
|
import Manga from './screens/Manga';
|
||||||
import Reader from './screens/Reader';
|
import Reader from './screens/Reader';
|
||||||
|
import Search from './screens/Search';
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
@@ -16,6 +17,9 @@ export default function App() {
|
|||||||
<NavBar />
|
<NavBar />
|
||||||
|
|
||||||
<Switch>
|
<Switch>
|
||||||
|
<Route path="/search">
|
||||||
|
<Search />
|
||||||
|
</Route>
|
||||||
<Route path="/extensions">
|
<Route path="/extensions">
|
||||||
<Extensions />
|
<Extensions />
|
||||||
</Route>
|
</Route>
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import MangaCard from './MangaCard';
|
||||||
|
|
||||||
|
interface IProps{
|
||||||
|
mangas: IManga[]
|
||||||
|
message?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MangaGrid(props: IProps) {
|
||||||
|
const { mangas, message } = props;
|
||||||
|
let mapped;
|
||||||
|
|
||||||
|
if (mangas.length === 0) {
|
||||||
|
mapped = <h3>{message !== undefined ? message : 'loading...'}</h3>;
|
||||||
|
} else {
|
||||||
|
mapped = (
|
||||||
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, auto)', gridGap: '1em' }}>
|
||||||
|
{mangas.map((it) => (
|
||||||
|
<MangaCard manga={it} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
@@ -48,6 +48,14 @@ export default function TemporaryDrawer({ drawerOpen, setDrawerOpen }: IProps) {
|
|||||||
<ListItemText primary="Sources" />
|
<ListItemText primary="Sources" />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link to="/search" style={{ color: 'inherit', textDecoration: 'none' }}>
|
||||||
|
<ListItem button key="Search">
|
||||||
|
<ListItemIcon>
|
||||||
|
<InboxIcon />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary="Global Search" />
|
||||||
|
</ListItem>
|
||||||
|
</Link>
|
||||||
</List>
|
</List>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import MangaCard from '../components/MangaCard';
|
import MangaGrid from '../components/MangaGrid';
|
||||||
|
|
||||||
export default function MangaList(props: { popular: boolean }) {
|
export default function MangaList(props: { popular: boolean }) {
|
||||||
const { sourceId } = useParams<{sourceId: string}>();
|
const { sourceId } = useParams<{sourceId: string}>();
|
||||||
let mapped;
|
|
||||||
const [mangas, setMangas] = useState<IManga[]>([]);
|
const [mangas, setMangas] = useState<IManga[]>([]);
|
||||||
const [lastPageNum] = useState<number>(1);
|
const [lastPageNum] = useState<number>(1);
|
||||||
|
|
||||||
@@ -17,17 +16,5 @@ export default function MangaList(props: { popular: boolean }) {
|
|||||||
));
|
));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (mangas.length === 0) {
|
return <MangaGrid mangas={mangas} />;
|
||||||
mapped = <h3>wait</h3>;
|
|
||||||
} else {
|
|
||||||
mapped = (
|
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, auto)', gridGap: '1em' }}>
|
|
||||||
{mangas.map((it) => (
|
|
||||||
<MangaCard manga={it} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mapped;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import React, { 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';
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
root: {
|
||||||
|
TextField: {
|
||||||
|
margin: theme.spacing(1),
|
||||||
|
width: '25ch',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default function Search() {
|
||||||
|
const classes = useStyles();
|
||||||
|
const [error, setError] = useState<boolean>(false);
|
||||||
|
const [mangas, setMangas] = useState<IManga[]>([]);
|
||||||
|
const [message, setMessage] = useState<string>('');
|
||||||
|
|
||||||
|
const textInput = React.createRef<HTMLInputElement>();
|
||||||
|
|
||||||
|
function doSearch() {
|
||||||
|
if (textInput.current) {
|
||||||
|
const { value } = textInput.current;
|
||||||
|
if (value === '') { setError(true); } else {
|
||||||
|
setError(false);
|
||||||
|
setMangas([]);
|
||||||
|
setMessage('button pressed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mangaGrid = <MangaGrid mangas={mangas} message={message} />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<form className={classes.root} noValidate autoComplete="off">
|
||||||
|
<TextField inputRef={textInput} error={error} id="standard-basic" label="Search text.." />
|
||||||
|
<Button variant="contained" color="primary" onClick={() => doSearch()}>
|
||||||
|
Primary
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
{mangaGrid}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user