Compare commits

...

173 Commits

Author SHA1 Message Date
b8350602c5 Finish master-merge 2025-06-18 19:29:23 +02:00
b4e25a9f54 Merge branch 'master' into vite-react-ts
# Conflicts:
#	Dockerfile
#	README.md
#	Website/apiConnector.js
#	Website/index.html
#	Website/interaction.js
2025-06-18 19:28:44 +02:00
5c78896a5d Add arm64 tag to vite-react-ts build
resolves #164
2025-06-18 19:22:40 +02:00
6bee507d93 Merge pull request #168 from C9Glax/cuttingedge
Last merge before v2 transition
2025-06-18 19:21:18 +02:00
7ec740da82 Merge branch 'master' into cuttingedge
# Conflicts:
#	.github/workflows/docker-image-vite-react-ts.yml
2025-06-18 19:20:20 +02:00
e253ae3d20 Code-Typos, Formatting, redundant statements 2025-06-18 19:04:17 +02:00
84f9dc176e Image Loading (MangaCovers) with lazy load attribute, onError instead of onLoad hook, preventing requests before sizing is complete 2025-06-18 18:58:46 +02:00
84e9d70d34 Merge pull request #167 from C9Glax/dependabot/github_actions/docker/setup-buildx-action-3.11.1
Bump docker/setup-buildx-action from 3.11.0 to 3.11.1
2025-06-18 18:11:33 +02:00
95f4086c24 Bump docker/setup-buildx-action from 3.11.0 to 3.11.1
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.11.0 to 3.11.1.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3.11.0...v3.11.1)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: 3.11.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-18 16:05:48 +00:00
4c81c571a4 Job Drawer add "Re-Run" button,
Move Chapter-Drawer to the bottom of the screen (inline with Manga-Drawer)
Format JobState and JobType (with spaces)
2025-06-18 17:51:29 +02:00
b4b4479e9b Open Links in new Tab 2025-06-18 02:04:52 +02:00
d6eeae4021 Add "/api" to default apiUriStr 2025-06-17 16:25:07 +02:00
6b05c4fb00 Fix MangaList Overflow, top-position 2025-06-17 15:40:25 +02:00
d785b6e078 Add ToolTip with Text-ReleaseStatus to Manga-Badge 2025-06-17 15:34:02 +02:00
e64f325022 Reorder Header items.
Center Logo
Move Settings/Jobs buttons to left side
2025-06-17 15:31:57 +02:00
486f6f60fb Add Links to useful ressources (GitHub, Swagger Doc) 2025-06-17 15:30:37 +02:00
01c732f6a8 Make header sticky to the top 2025-06-17 15:20:59 +02:00
e5641e0690 Fix Size of SearchCard 2025-06-17 01:47:08 +02:00
b7ee80b4a4 Re-Add Screenshots 2025-06-17 01:22:13 +02:00
9f8d874822 Fix Header Spacing and Badge Location if Backend can't be reached 2025-06-17 01:11:19 +02:00
a273af5ed9 FlareSolverr Settings 2025-06-17 00:55:47 +02:00
be704d922a Add a DO NOT REPORT disclaimer to "Already Requested" message 2025-06-16 22:48:45 +02:00
692bf3561b Fix Request being wrongly resolved (even if it failed) 2025-06-16 22:47:53 +02:00
e0ec64882b URL Search
Fix MangaConnector color
2025-06-16 22:30:33 +02:00
496e6fdde8 Fix MangaPopup missing setOpen 2025-06-16 21:23:07 +02:00
49fe38962d Merge pull request #166 from C9Glax/dependabot/github_actions/docker/setup-buildx-action-3.11.0
Bump docker/setup-buildx-action from 3.10.0 to 3.11.0
2025-06-16 19:38:34 +02:00
645d3c8793 Fix Jobs Popup:
- Loading without being opened
- Cluttered view: Make it a table
2025-06-16 19:37:10 +02:00
2ef7ee6b39 Bump docker/setup-buildx-action from 3.10.0 to 3.11.0
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.10.0 to 3.11.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3.10.0...v3.11.0)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: 3.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-16 17:18:05 +00:00
e1b590482c Nginx route requests to api via nginx 2025-06-16 00:24:51 +02:00
94e58d2d1b Fix MangaConnectorContext (missing .Provider) 2025-06-15 23:14:11 +02:00
d5c85efcb2 Fix vite host address (localhost) 2025-06-15 23:11:09 +02:00
aed0a562f0 Remove old screenshots 2025-06-15 23:04:48 +02:00
46729bade3 Merge pull request #165 from C9Glax/dependabot/github_actions/docker/build-push-action-6.18.0
Bump docker/build-push-action from 6.17.0 to 6.18.0
2025-05-28 17:17:40 +02:00
fed7ff987e Bump docker/build-push-action from 6.17.0 to 6.18.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.17.0 to 6.18.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.17.0...v6.18.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: 6.18.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-28 15:16:47 +00:00
ddaa066dc7 Jobs List 2025-05-20 03:14:37 +02:00
a6d2de35cf Jobs List 2025-05-20 03:12:51 +02:00
7cdc31fedb Fix ImageProcessing 2025-05-19 20:52:47 +02:00
dcf596df84 Fix AprilFoolsMode Setting 2025-05-19 20:23:31 +02:00
855fe91e69 Fix UserAgent Setting 2025-05-19 20:23:24 +02:00
c00c80503c Fix UserAgent Setting 2025-05-19 20:19:18 +02:00
4e648bd04e MangaPopup placeholder for Chapterdownload: maxdownloadedchapter 2025-05-19 20:10:58 +02:00
35e2e6ce16 MangaPopup (from List) add "Last Execution" tooltip 2025-05-19 20:06:26 +02:00
dbdb3c0168 Add "Start-Job" button to MangaPopup (from list) 2025-05-19 20:00:03 +02:00
dae653c2b4 Update Job-Dependency (backend) 2025-05-19 19:59:48 +02:00
e9a2a561cf fetchApi.tsx fix nullable content-body for postData and wrapper 2025-05-19 19:58:56 +02:00
5fe3ead54d Move the Start-Search Card to MangaList.tsx 2025-05-19 19:33:39 +02:00
6d8d0ec636 Move Popup-Tags to MangaPopup.tsx 2025-05-19 19:29:42 +02:00
724dc9b986 MangaList show Skeleton instead of empty space for loading manga
MangaCard remove Link from name, move it to Popup
2025-05-19 19:23:00 +02:00
a59920f09d Fix results null 2025-05-18 22:42:52 +02:00
5c5f810127 MangaPopup Badge Icon 2025-05-18 22:38:40 +02:00
181d591344 useState for MangaConnector in Manga with loading from id 2025-05-18 22:26:06 +02:00
0193077377 MangaConnectorContext 2025-05-18 22:01:21 +02:00
9fa57eacdb Fix BadgeIcon-Size 2025-05-18 21:57:27 +02:00
c4cc21f866 Dependency Update 2025-05-18 20:34:47 +02:00
f391ace9b2 fetchApi dont throw rejected promises 2025-05-18 19:52:38 +02:00
a0d15e08a7 Manga Card with Icon as Badge 2025-05-18 17:28:01 +02:00
11549a07c3 MangaPopup Drawer 2025-05-17 19:25:31 +02:00
79c13ceb1d Fix Manga-Item to new interfaces,
fix Manga-cover
2025-05-17 18:29:21 +02:00
0907031583 API Dependency-Update 2025-05-17 17:59:30 +02:00
00932bf6bd Remove Lunasea 2025-05-16 19:24:00 +02:00
594103689e Merge pull request #162 from C9Glax/dependabot/github_actions/docker/build-push-action-6.17.0
Bump docker/build-push-action from 6.16.0 to 6.17.0
2025-05-15 18:00:45 +02:00
6ab431fde0 Bump docker/build-push-action from 6.16.0 to 6.17.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.16.0 to 6.17.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.16.0...v6.17.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: 6.17.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-15 15:15:48 +00:00
bbad467fe6 Cutoff MangaName in CoverCard 2025-05-08 05:26:51 +02:00
03c78f7b6a Cardsizing in const 2025-05-08 05:25:36 +02:00
3ad7fbfad2 Make ApiUri persistent 2025-05-08 05:23:31 +02:00
6173d16edc MangaCard fix Margins 2025-05-08 05:12:36 +02:00
8a07818b89 MangaCard fix Margins 2025-05-08 05:02:46 +02:00
6aa53f7ca7 Fix some sizing on MangaCard 2025-05-08 04:55:06 +02:00
415e6c606f Make Manga-Cards smaller,
Manga-Cover covers whole Card
2025-05-08 04:40:09 +02:00
178e8b72f3 Remove debug 2025-05-08 02:29:08 +02:00
65457c289f Favicon and Page-Title 2025-05-08 02:23:48 +02:00
6109bb678a Reload Cover/Latest available on load 2025-05-08 02:20:39 +02:00
cc14c5adad Debug 2025-05-08 02:20:24 +02:00
8c0f61fc6a update dockerfile 2025-05-08 02:20:24 +02:00
82389c4847 installed @uiw/react-markdown-preview 2025-05-08 01:22:52 +02:00
a91452ee8c Update to mui 2025-05-08 01:12:48 +02:00
698e16cc2b Update to mui 2025-05-08 00:57:15 +02:00
4e20b95d77 Merge pull request #161 from C9Glax/ant-design
React with mui/joy
2025-05-08 00:46:21 +02:00
18257cd584 Update workflow-files 2025-05-08 00:45:32 +02:00
5626a9d0fd Remove Lunasea https://github.com/C9Glax/tranga/issues/389 2025-05-08 00:32:09 +02:00
c6dc3cd107 Merge pull request #160 from C9Glax/dependabot/github_actions/docker/build-push-action-6.16.0
Bump docker/build-push-action from 6.15.0 to 6.16.0
2025-04-24 19:54:29 +02:00
94c47b904d Bump docker/build-push-action from 6.15.0 to 6.16.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.15.0 to 6.16.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.15.0...v6.16.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: 6.16.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-24 15:53:47 +00:00
10f48af9fa Styling 2025-04-02 02:17:10 +02:00
5e95c34306 Reset Step to 2 on search drawer close if greater 2 2025-04-02 01:22:50 +02:00
66dcb2f1e6 RequestLimits Reset All position 2025-04-02 01:20:00 +02:00
19e033995d Search Styling 2025-04-02 01:18:49 +02:00
99265bacb2 UserAgent Reset 2025-04-02 01:06:55 +02:00
5385dfd918 Request Limits Reset 2025-04-02 01:04:37 +02:00
d6e4d1d27f Request Limits 2025-04-02 00:52:35 +02:00
288cd77049 April Fools Mode 2025-04-02 00:19:26 +02:00
e605578a34 Chapter Naming Scheme Settings 2025-04-02 00:16:13 +02:00
6340c5ad03 Image Processing Settings 2025-04-02 00:06:57 +02:00
6d402357e7 UserAgent Setting 2025-04-01 19:17:46 +02:00
9e0eb0262e MangaList 2025-04-01 19:05:57 +02:00
63b220bd79 Add Badge to Search Results to show number of results 2025-04-01 18:29:27 +02:00
d480f62e51 Warning icon if API not connected 2025-04-01 18:16:57 +02:00
44755675e5 LocalLibrary Select in Manga Search 2025-04-01 18:10:30 +02:00
6b10aa8926 @mui/joy 2025-04-01 04:13:50 +02:00
48b669dc07 asset include 2025-03-30 17:34:58 +02:00
fbb25807e6 asset include 2025-03-30 17:32:50 +02:00
44f43b7152 asset include 2025-03-30 17:23:23 +02:00
68653778c8 wrong reference 2025-03-30 17:21:21 +02:00
fd4c01203f Build app before deploy
https://github.com/C9Glax/tranga/pull/355#issuecomment-2764253691
2025-03-30 17:10:25 +02:00
2a4316cf1d Update README.md 2025-03-29 21:17:46 +01:00
d358611133 https://github.com/C9Glax/tranga/issues/246 2025-03-29 21:00:33 +01:00
b0cbb416d6 Custom Chapter Naming Scheme
https://github.com/C9Glax/tranga/issues/92
2025-03-29 20:34:09 +01:00
e3ad70c0a9 Settings to enable and disable MangaConnectors 2025-03-27 19:50:41 +01:00
f38290b2ed #50 2025-03-27 19:40:38 +01:00
211db3d4d5 Only make 1 request to an endpoint concurrently 2025-03-20 00:58:12 +01:00
a3046680ac Update Job-Footer only every 5 seconds 2025-03-20 00:57:53 +01:00
1ed376ba47 Do not update Queue-preview when not shown 2025-03-20 00:57:23 +01:00
63b10600da README 2025-03-20 00:43:58 +01:00
5333c025f7 Fix loaders 2025-03-20 00:41:35 +01:00
187dd22027 Backend Settings 2025-03-20 00:07:20 +01:00
2092db2ba3 Local Libraries in Settings 2025-03-19 22:44:11 +01:00
ecd76712d8 Add all types of Notification Connectors to settings 2025-03-19 22:01:07 +01:00
89586ef891 Merge pull request #158 from C9Glax/dependabot/github_actions/docker/setup-qemu-action-3.6.0
Bump docker/setup-qemu-action from 3.3.0 to 3.6.0
2025-03-13 14:57:25 +01:00
33514ee375 Merge pull request #156 from C9Glax/dependabot/github_actions/docker/setup-buildx-action-3.10.0
Bump docker/setup-buildx-action from 3.8.0 to 3.10.0
2025-03-13 14:57:11 +01:00
526e129fb7 Merge pull request #155 from C9Glax/dependabot/github_actions/docker/build-push-action-6.15.0
Bump docker/build-push-action from 6.14.0 to 6.15.0
2025-03-13 14:56:56 +01:00
d1dae83387 Bump docker/setup-qemu-action from 3.3.0 to 3.6.0
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.3.0 to 3.6.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v3.3.0...v3.6.0)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-28 15:50:31 +00:00
9199a7a0e4 Bump docker/setup-buildx-action from 3.8.0 to 3.10.0
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.8.0 to 3.10.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3.8.0...v3.10.0)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-26 15:51:05 +00:00
d9fa4185e2 Bump docker/build-push-action from 6.14.0 to 6.15.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.14.0 to 6.15.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.14.0...v6.15.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-26 15:51:03 +00:00
717c8cd33e Merge pull request #154 from C9Glax/dependabot/github_actions/docker/build-push-action-6.14.0
Bump docker/build-push-action from 6.13.0 to 6.14.0
2025-02-20 17:13:39 +01:00
483c2d564b Bump docker/build-push-action from 6.13.0 to 6.14.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.13.0 to 6.14.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.13.0...v6.14.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-20 15:59:01 +00:00
5807844281 Merge pull request #148 from C9Glax/dependabot/github_actions/docker/build-push-action-6.13.0
Bump docker/build-push-action from 6.12.0 to 6.13.0
2025-01-24 22:17:05 +01:00
77d9e6eee1 Bump docker/build-push-action from 6.12.0 to 6.13.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.12.0 to 6.13.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.12.0...v6.13.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-24 15:42:12 +00:00
e45e7bd5f5 Merge pull request #147 from C9Glax/dependabot/github_actions/docker/build-push-action-6.12.0
Bump docker/build-push-action from 6.11.0 to 6.12.0
2025-01-15 17:44:02 +01:00
ccab3a8027 Bump docker/build-push-action from 6.11.0 to 6.12.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.11.0 to 6.12.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.11.0...v6.12.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-15 15:39:53 +00:00
5eca8dac5d Merge pull request #145 from C9Glax/dependabot/github_actions/docker/build-push-action-6.11.0
Bump docker/build-push-action from 6.9.0 to 6.11.0
2025-01-09 01:53:33 +01:00
0b34ca7723 Merge pull request #146 from C9Glax/dependabot/github_actions/docker/setup-qemu-action-3.3.0
Bump docker/setup-qemu-action from 3.2.0 to 3.3.0
2025-01-09 01:53:16 +01:00
eb943ccbed Merge pull request #143 from C9Glax/dependabot/github_actions/docker/setup-buildx-action-3.8.0
Bump docker/setup-buildx-action from 3.7.1 to 3.8.0
2025-01-09 01:53:02 +01:00
514cd06d2b Bump docker/setup-qemu-action from 3.2.0 to 3.3.0
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 3.3.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v3.2.0...v3.3.0)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-08 15:36:31 +00:00
2035bc8f4e Bump docker/build-push-action from 6.9.0 to 6.11.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.9.0 to 6.11.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.9.0...v6.11.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-08 15:36:29 +00:00
ece175d1db Bump docker/setup-buildx-action from 3.7.1 to 3.8.0
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.7.1 to 3.8.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3.7.1...v3.8.0)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 16:17:56 +00:00
da3867962b Merge pull request #141 from C9Glax/dependabot/github_actions/docker/build-push-action-6.9.0
Bump docker/build-push-action from 6.7.0 to 6.9.0
2024-11-04 16:27:26 +01:00
ec15732e57 Merge pull request #140 from C9Glax/dependabot/github_actions/docker/setup-buildx-action-3.7.1
Bump docker/setup-buildx-action from 3.6.1 to 3.7.1
2024-11-04 16:27:18 +01:00
6d10c81dff Bump docker/build-push-action from 6.7.0 to 6.9.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.7.0 to 6.9.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.7.0...v6.9.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-04 15:26:40 +00:00
a3842ba20f Bump docker/setup-buildx-action from 3.6.1 to 3.7.1
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.6.1 to 3.7.1.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3.6.1...v3.7.1)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-04 15:26:38 +00:00
74b71d57b7 Merge pull request #117 from vonProteus/defaultApiUrl
Ability to override defaultApiUrl
2024-11-03 02:59:35 +01:00
6a5e340882 Merge branch 'cuttingedge' into defaultApiUrl 2024-11-03 02:58:39 +01:00
95801828c6 Dont build on pull 2024-10-19 19:54:52 +02:00
b70dfbf482 Update docker-image-cuttingedge.yml 2024-09-11 14:50:44 +02:00
0bc93a7e5c Merge remote-tracking branch 'github/master' into cuttingedge 2024-09-11 14:43:18 +02:00
ddd9512469 Merge pull request #119 from C9Glax/master
Merge Github workflows
2024-09-09 20:05:08 +02:00
c26e208822 Update docker-image-cuttingedge.yml 2024-09-09 20:04:16 +02:00
c656e5face Update docker-image-dev.yml 2024-09-09 20:03:57 +02:00
4addacb229 Update docker-image-master.yml 2024-09-09 20:03:08 +02:00
c1aece8200 Ability to override defaultApiUrl 2024-09-01 06:09:13 +02:00
6bd9a0b1db Decrease update frequency of website 2024-08-26 12:47:46 +02:00
c72e152a7e Merge branch 'master' of github.com:C9Glax/tranga-website into cuttingedge 2024-04-22 21:56:29 +02:00
38572a3a46 Fixes https://github.com/C9Glax/tranga/issues/171 Overwrites for settingsg that weren't changed on the frontend. 2024-04-22 21:53:12 +02:00
5b52577610 Merge pull request #82 from db-2001/remove-mdex-author
Remove Mangadex Author Rate Limit
2024-04-18 23:54:42 +02:00
3b99dbd487 Remove Mangadex Author Rate Limit 2024-04-18 17:33:08 -04:00
3eb7b63fa6 Fix https://github.com/C9Glax/tranga-website/issues/76 wrong context 2024-04-02 02:07:04 +02:00
793561dad7 Merge remote-tracking branch 'github/cuttingedge' into cuttingedge 2024-04-02 02:06:04 +02:00
acb22d770d Merge pull request #74 from C9Glax/dev
No longer need to set API-URI manually
2024-03-30 00:16:47 -04:00
e0093c65ff Merge pull request #75 from C9Glax/dev
No longer need to set API-URI manually
2024-03-30 00:16:37 -04:00
ecf9e1a243 Merge pull request #69 from YodaDaCoda/dev
feat: enable API_URL in Dockerfile
2024-03-06 21:12:21 -05:00
=
5af3005179 feat: enable API_URL in Dockerfile
fix tranga.conf not redirecting /api/ requests
2024-03-07 12:16:19 +11:00
d33ac16c7d Merge pull request #68 from db-2001/dev
another freaking semicolon mistake
2024-03-06 00:11:17 -05:00
a2e0a2375a another freaking semicolon mistake 2024-03-06 00:10:33 -05:00
9c0a7a0a50 Merge pull request #67 from db-2001/dev
more conf tweaks
2024-03-06 00:07:55 -05:00
d05b8ea76b more conf tweaks 2024-03-06 00:06:16 -05:00
e9937c02bf Merge pull request #66 from db-2001/dev
conf tweaks
2024-03-05 23:55:34 -05:00
ef5524d7b6 conf tweaks 2024-03-05 23:53:46 -05:00
8f9700ec02 Merge branch 'dev' of https://github.com/C9Glax/tranga-website into dev 2024-03-05 23:09:26 -05:00
4b86cd7104 A little fucking semi-colon 2024-03-05 23:09:17 -05:00
61e3fbd500 Merge pull request #65 from db-2001/dev
Reverse Proxy api automatically test
2024-03-05 23:05:41 -05:00
657a3d1857 Reverse Proxy api automatically test 2024-03-05 23:04:11 -05:00
b4cf22b6eb Merge pull request #63 from C9Glax/cuttingedge
Updated github workflows
2024-03-01 14:08:17 -05:00
2216e13c49 Merge pull request #62 from C9Glax/dev
Updated the docker workflow files
2024-03-01 14:07:29 -05:00
0a68da2b49 Fix docker workflow files for cuttingedge and dev branches 2024-03-01 13:48:20 -05:00
b87c159823 Merge pull request #61 from C9Glax/master
Updating Dev branch so we can actually start using it for dev things
2024-02-28 06:23:18 -05:00
189 changed files with 9317 additions and 6256 deletions

View File

@ -3,6 +3,8 @@ name: Docker Image CI
on:
push:
branches: [ "cuttingedge" ]
pull_request:
branches: [ "cuttingedge" ]
workflow_dispatch:
jobs:
@ -17,12 +19,12 @@ jobs:
# https://github.com/docker/setup-qemu-action#usage
- name: Set up QEMU
uses: docker/setup-qemu-action@v3.2.0
uses: docker/setup-qemu-action@v3.6.0
# https://github.com/marketplace/actions/docker-setup-buildx
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3.6.1
uses: docker/setup-buildx-action@v3.11.1
# https://github.com/docker/login-action#docker-hub
- name: Login to Docker Hub
@ -33,12 +35,12 @@ jobs:
# https://github.com/docker/build-push-action#multi-platform-image
- name: Build and push Website
uses: docker/build-push-action@v6.7.0
uses: docker/build-push-action@v6.18.0
with:
context: ./Website
context: .
file: ./Dockerfile
#platforms: linux/amd64,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6
platforms: linux/amd64
platforms: linux/amd64,linux/arm64
pull: true
push: true
tags: |

View File

@ -17,12 +17,12 @@ jobs:
# https://github.com/docker/setup-qemu-action#usage
- name: Set up QEMU
uses: docker/setup-qemu-action@v3.2.0
uses: docker/setup-qemu-action@v3.6.0
# https://github.com/marketplace/actions/docker-setup-buildx
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3.6.1
uses: docker/setup-buildx-action@v3.11.1
# https://github.com/docker/login-action#docker-hub
- name: Login to Docker Hub
@ -33,12 +33,12 @@ jobs:
# https://github.com/docker/build-push-action#multi-platform-image
- name: Build and push Website
uses: docker/build-push-action@v6.7.0
uses: docker/build-push-action@v6.18.0
with:
context: ./Website
context: ./
file: ./Dockerfile
#platforms: linux/amd64,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6
platforms: linux/amd64
platforms: linux/amd64,linux/arm64
pull: true
push: true
tags: |

View File

@ -17,12 +17,12 @@ jobs:
# https://github.com/docker/setup-qemu-action#usage
- name: Set up QEMU
uses: docker/setup-qemu-action@v3.2.0
uses: docker/setup-qemu-action@v3.6.0
# https://github.com/marketplace/actions/docker-setup-buildx
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3.6.1
uses: docker/setup-buildx-action@v3.11.1
# https://github.com/docker/login-action#docker-hub
- name: Login to Docker Hub
@ -33,12 +33,12 @@ jobs:
# https://github.com/docker/build-push-action#multi-platform-image
- name: Build and push Website
uses: docker/build-push-action@v6.7.0
uses: docker/build-push-action@v6.18.0
with:
context: ./Website
context: .
file: ./Dockerfile
#platforms: linux/amd64,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6
platforms: linux/amd64
platforms: linux/amd64,linux/arm64
pull: true
push: true
tags: |

View File

@ -1,4 +1,19 @@
FROM nginx:alpine3.17-slim
COPY . /usr/share/nginx/html
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY ./tranga-website /app
RUN npm install
RUN npm run build
# Serve stage
FROM nginx:alpine3.17-slim
# Copy built files from Vite's dist folder
COPY --from=builder /app/dist /usr/share/nginx/html
#COPY --from=builder /app/tranga-website/media /usr/share/nginx/html/media
COPY ./nginx /etc/nginx
EXPOSE 80
ENV API_URL=http://tranga-api:6531
CMD ["nginx", "-g", "daemon off;"]

View File

@ -4,7 +4,7 @@
<h3 align="center">Tranga-Website</h3>
<p align="center">
Automatic MangaFunctions and Metadata downloader
Automatic Manga and Metadata downloader
</p>
<p align="center">
This is the Website for <a href="https://github.com/C9Glax/tranga">Tranga</a> (API)
@ -33,19 +33,9 @@
<!-- ABOUT THE PROJECT -->
## Screenshots
| Default View | Search Window | Search Results |
|-------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|
| ![Image](Screenshots/Screenshot%202025-03-19%20at%2002-38-47%20Tranga.png) | ![Image](Screenshots/Screenshot%202025-03-19%20at%2002-39-05%20Tranga.png)<br/>![Image](Screenshots/Screenshot%202025-03-19%20at%2002-39-45%20Tranga.png) | ![Image](Screenshots/Screenshot%202025-03-19%20at%2002-39-52%20Tranga.png)<br/>![Image](Screenshots/Screenshot%202025-03-19%20at%2002-39-58%20Tranga.png) |
| Search opens with click on "Add new Manga".<br/>Settings are on the top right | When selecting different connectors, available languages automatically update.<br/>Spinners to indicate action being performed | Clicking on an Item here will bring up a view with more information |
| | Different Views for Manga | |
|----------------------------------------------------------------------------|----------------------------------------------------------------------------|----------------------------------------------------------------------------|
| ![Image](Screenshots/Screenshot%202025-03-19%20at%2002-41-51%20Tranga.png) | ![Image](Screenshots/Screenshot%202025-03-19%20at%2002-42-02%20Tranga.png) | ![Image](Screenshots/Screenshot%202025-03-19%20at%2002-42-12%20Tranga.png) |
| | Rudimentary Settings | |
|-|----------------------------------------------------------------------------|-|
| | ![Image](Screenshots/Screenshot%202025-03-19%20at%2002-42-21%20Tranga.png) | |
| ![Main Page](Screenshots/Screenshot%202025-06-17%20at%2001-15-08%20Tranga.png) | ![Settings](Screenshots/Screenshot%202025-06-17%20at%2001-15-23%20Tranga.png) | ![Search Empty](Screenshots/Screenshot%202025-06-17%20at%2001-15-36%20Tranga.png) |
|-------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|
| ![Search Results](Screenshots/Screenshot%202025-06-17%20at%2001-16-32%20Tranga.png) | ![Manga Popup](Screenshots/Screenshot%202025-06-17%20at%2001-16-43%20Tranga.png) | ![Jobs Drawer](Screenshots/Screenshot%202025-06-17%20at%2001-17-37%20Tranga.png) |
## About The Project

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 508 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 539 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 876 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 890 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 KiB

View File

@ -1,150 +0,0 @@
import React, {useEffect, useState} from 'react';
import Footer from "./modules/Footer";
import Search from "./modules/Search";
import Header from "./modules/Header";
import MonitorJobsList from "./modules/MonitorJobsList";
import './styles/index.css'
import IFrontendSettings, {LoadFrontendSettings} from "./modules/interfaces/IFrontendSettings";
import {useCookies} from "react-cookie";
import Loader from "./modules/Loader";
export default function App(){
const [, setCookie] = useCookies(['apiUri', 'jobInterval']);
const [connected, setConnected] = React.useState(false);
const [showSearch, setShowSearch] = React.useState(false);
const [frontendSettings, setFrontendSettings] = useState<IFrontendSettings>(LoadFrontendSettings());
const [updateInterval, setUpdateInterval] = React.useState<number | undefined>(undefined);
const checkConnectedInterval = 1000;
const apiUri = frontendSettings.apiUri;
useEffect(() => {
setCookie('apiUri', frontendSettings.apiUri);
setCookie('jobInterval', frontendSettings.jobInterval);
updateConnected(apiUri, connected, setConnected);
}, [frontendSettings]);
useEffect(() => {
if(updateInterval === undefined){
setUpdateInterval(setInterval(() => {
updateConnected(apiUri, connected, setConnected);
}, checkConnectedInterval));
}else{
clearInterval(updateInterval);
setUpdateInterval(undefined);
}
}, [connected]);
return(<div>
<Header apiUri={apiUri} backendConnected={connected} settings={frontendSettings} setFrontendSettings={setFrontendSettings} />
{connected
? <>
{showSearch
? <>
<Search apiUri={apiUri} jobInterval={frontendSettings.jobInterval} closeSearch={() => setShowSearch(false)} />
<hr/>
</>
: <></>}
<MonitorJobsList apiUri={apiUri} onStartSearch={() => setShowSearch(true)} connectedToBackend={connected} checkConnectedInterval={checkConnectedInterval} />
</>
: <>
<h1>No connection to the Backend.</h1>
<h3>Check the Settings ApiUri.</h3>
<Loader loading={true} />
</>}
<Footer apiUri={apiUri} connectedToBackend={connected} checkConnectedInterval={checkConnectedInterval} />
</div>)
}
export function getData(uri: string) : Promise<object> {
return makeRequest("GET", uri, null) as Promise<object>;
}
export function postData(uri: string, content: object | string | number) : Promise<object> {
return makeRequest("POST", uri, content) as Promise<object>;
}
export function deleteData(uri: string) : Promise<void> {
return makeRequest("DELETE", uri, null) as Promise<void>;
}
export function patchData(uri: string, content: object | string | number) : Promise<object> {
return makeRequest("patch", uri, content) as Promise<object>;
}
export function putData(uri: string, content: object | string | number) : Promise<object> {
return makeRequest("PUT", uri, content) as Promise<object>;
}
function makeRequest(method: string, uri: string, content: object | string | number | null) : Promise<object | void> {
return fetch(uri,
{
method: method,
headers : {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: content ? JSON.stringify(content) : null
})
.then(function(response){
if(!response.ok){
if(response.status === 503){
let retryHeaderVal = response.headers.get("Retry-After");
let seconds = 10;
if(!retryHeaderVal){
return response.text().then(text => {
seconds = parseInt(text);
return new Promise(resolve => setTimeout(resolve, seconds * 1000))
.then(() => {
return makeRequest(method, uri, content);
});
});
}else {
seconds = parseInt(retryHeaderVal);
return new Promise(resolve => setTimeout(resolve, seconds * 1000))
.then(() => {
return makeRequest(method, uri, content);
});
}
}else
throw new Error(response.statusText);
}
let json = response.json();
return json.then((json) => json).catch(() => null);
})
.catch(function(err : Error){
console.error(`Error ${method}ing Data ${uri}\n${err}`);
return Promise.reject();
});
}
export function isValidUri(uri: string) : boolean{
try {
new URL(uri);
return true;
} catch (err) {
return false;
}
}
const updateConnected = (apiUri: string, connected: boolean, setConnected: (c: boolean) => void) => {
checkConnection(apiUri)
.then(res => {
if(connected != res)
setConnected(res);
})
.catch(() => setConnected(false));
}
export const checkConnection = async (apiUri: string): Promise<boolean> =>{
return fetch(`${apiUri}/swagger`,
{
method: 'GET',
})
.then((response) =>{
return response.type != "error";
})
.catch(() => {
return Promise.reject();
});
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 440 B

View File

@ -1,113 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="512pt"
viewBox="0 0 512 512"
width="512pt"
version="1.1"
id="svg4586"
sodipodi:docname="komga - Copy.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata4592">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs4590">
<linearGradient
id="linearGradient6082"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop6080"/>
</linearGradient>
<linearGradient
id="linearGradient6076"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop6074"/>
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient6082"
id="linearGradient6084"
x1="77.866812"
y1="386.00679"
x2="217.20259"
y2="386.00679"
gradientUnits="userSpaceOnUse"/>
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1656"
inkscape:window-height="1368"
id="namedview4588"
showgrid="false"
inkscape:zoom="1.2512475"
inkscape:cx="264.73114"
inkscape:cy="305.20589"
inkscape:window-x="-7"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg4586"/>
<path
d="m512 256c0 141.386719-114.613281 256-256 256s-256-114.613281-256-256 114.613281-256 256-256 256 114.613281 256 256zm0 0"
fill="#005ed3"
id="path4556"/>
<path
d="m 512,256 c 0,-11.71094 -0.80469,-23.23047 -2.32422,-34.52344 L 382.48047,94.28125 320.52344,121.85938 256,56.933594 212.69531,131.30469 129.51953,94.28125 141.86719,178.42187 49.949219,193.81641 114.32031,256 l -64.371091,62.18359 82.121091,82.16016 -2.55078,17.375 91.95703,91.95703 C 232.76953,511.19531 244.28906,512 256,512 397.38672,512 512,397.38672 512,256 Z"
id="path4558"
inkscape:connector-curvature="0"
style="fill:#00459f"
sodipodi:nodetypes="scccccccccccccss"/>
<path
d="m256 86.742188 37.109375 63.738281 70.574219-31.414063-10.527344 71.71875 77.078125 12.910156-54.144531 52.304688 54.144531 52.304688-77.078125 12.910156 10.527344 71.71875-70.574219-31.414063-37.109375 63.738281-37.109375-63.738281-70.574219 31.414063 10.527344-71.71875-77.078125-12.910156 54.144531-52.304688-54.144531-52.304688 77.078125-12.910156-10.527344-71.71875 70.574219 31.414063zm0 0"
fill="#ff0335"
id="path4560"/>
<path
d="m430.230469 308.300781-77.070313 12.910157 10.519532 71.71875-70.570313-31.410157-37.109375 63.742188v-338.523438l37.109375 63.742188 70.570313-31.410157-6.757813 46.101563-3.761719 25.617187 58.800782 9.851563 18.269531 3.058594-13.390625 12.929687-40.75 39.371094 11.378906 10.988281zm0 0"
fill="#c2001b"
id="path4562"/>
<path
d="m256 455.066406-43.304688-74.371094-83.175781 37.023438 12.347657-84.140625-91.917969-15.394531 64.371093-62.183594-64.371093-62.183594 91.917969-15.394531-12.347657-84.140625 83.179688 37.023438 43.300781-74.371094 43.304688 74.371094 83.175781-37.023438-12.347657 84.140625 91.917969 15.394531-64.371093 62.183594 64.371093 62.183594-91.917969 15.398437 12.347657 84.136719-83.175781-37.023438zm-30.917969-112.722656 30.917969 53.101562 30.917969-53.101562 57.964843 25.800781-8.703124-59.292969 62.238281-10.425781-43.917969-42.425781 43.917969-42.425781-62.238281-10.425781 8.703124-59.292969-57.964843 25.800781-30.917969-53.101562-30.917969 53.101562-57.964843-25.800781 8.703124 59.292969-62.238281 10.425781 43.917969 42.425781-43.917969 42.425781 62.238281 10.425781-8.703124 59.292969zm0 0"
fill="#ffdf47"
id="path4564"/>
<path
d="m403.308594 261.441406-5.628906-5.441406 25.160156-24.300781 39.210937-37.878907-55.75-9.339843-36.171875-6.058594 2.800782-19.09375 9.550781-65.046875-83.179688 37.019531-43.300781-74.371093v59.621093l30.921875 53.109375 57.957031-25.808594-3.910156 26.667969-2.546875 17.378907-2.242187 15.25 2.480468.421874 59.761719 10.007813-43.921875 42.421875 16.96875 16.390625 26.953125 26.03125-62.242187 10.429687 8.699218 59.296876-57.957031-25.808594-30.921875 53.109375v59.621093l43.300781-74.371093 83.179688 37.019531-12.351563-84.140625 91.921875-15.398437zm0 0"
fill="#fec000"
id="path4566"/>
<g
aria-label="K"
transform="matrix(1.1590846,-0.34467221,0.22789693,0.794981,0,0)"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:296.55969238px;line-height:125%;font-family:Impact;-inkscape-font-specification:Impact;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.54528999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="text4596">
<path
d="m 220.91497,266.9035 -34.89789,105.85211 38.2284,128.58643 H 161.2555 L 136.63873,400.84769 V 501.34204 H 75.676021 V 266.9035 h 60.962709 v 91.08205 l 27.07845,-91.08205 z"
style="font-size:296.55969238px;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.54528999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path824"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -1,40 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="50mm" height="50mm" viewBox="0 0 50 50">
<defs>
<linearGradient id="b">
<stop offset="0" style="stop-color:#348878;stop-opacity:1"/>
<stop offset="1" style="stop-color:#52bca6;stop-opacity:1"/>
</linearGradient>
<linearGradient id="a">
<stop offset="0" style="stop-color:#348878;stop-opacity:1"/>
<stop offset="1" style="stop-color:#56bda8;stop-opacity:1"/>
</linearGradient>
<linearGradient xlink:href="#a" id="e" x1="160.722" x2="168.412" y1="128.533" y2="134.326" gradientTransform="matrix(3.74959 0 0 3.74959 -541.79 -387.599)" gradientUnits="userSpaceOnUse"/>
<linearGradient xlink:href="#b" id="c" x1=".034" x2="50.319" y1="0" y2="50.285" gradientTransform="matrix(.99434 0 0 .99434 -.034 0)" gradientUnits="userSpaceOnUse"/>
<filter id="d" width="1.176" height="1.211" x="-.076" y="-.092" style="color-interpolation-filters:sRGB">
<feFlood flood-color="#fff" flood-opacity=".192" result="flood"/>
<feComposite in="flood" in2="SourceGraphic" operator="in" result="composite1"/>
<feGaussianBlur in="composite1" result="blur" stdDeviation="4"/>
<feOffset dx="3" dy="2.954" result="offset"/>
<feComposite in="SourceGraphic" in2="offset" result="composite2"/>
</filter>
</defs>
<g style="display:inline">
<path d="M0 0h50v50H0z" style="fill:url(#c);fill-opacity:1;stroke:none;stroke-width:.286502;stroke-linejoin:bevel"/>
</g>
<g style="display:inline">
<path d="M50.4 46.883c-9.168 0-17.023 7.214-17.023 16.387v.007l.09 71.37-2.303 16.992 31.313-8.319h77.841c9.17 0 17.024-7.224 17.024-16.396V63.27c0-9.17-7.85-16.383-17.016-16.387h-.008zm0 11.566h89.926c3.222.004 5.45 2.347 5.45 4.82v63.655c0 2.475-2.232 4.82-5.457 4.82h-79.54l-15.908 4.807.162-.938-.088-72.343c0-2.476 2.23-4.82 5.455-4.82z" style="color:#fff;display:inline;fill:#fff;stroke:none;stroke-width:1.93113;-inkscape-stroke:none;filter:url(#d)" transform="scale(.26458)"/>
</g>
<g style="display:inline">
<path d="M88.2 95.309H64.92c-1.601 0-2.91 1.236-2.91 2.746l.022 18.602-.435 2.506 6.231-1.881H88.2c1.6 0 2.91-1.236 2.91-2.747v-16.48c0-1.51-1.31-2.746-2.91-2.746z" style="color:#fff;fill:url(#e);stroke:none;stroke-width:2.49558;-inkscape-stroke:none" transform="translate(-51.147 -81.516)"/>
<path d="M50.4 46.883c-9.168 0-17.023 7.214-17.023 16.387v.007l.09 71.37-2.303 16.992 31.313-8.319h77.841c9.17 0 17.024-7.224 17.024-16.396V63.27c0-9.17-7.85-16.383-17.016-16.387h-.008zm0 11.566h89.926c3.222.004 5.45 2.347 5.45 4.82v63.655c0 2.475-2.232 4.82-5.457 4.82h-79.54l-15.908 4.807.162-.938-.088-72.343c0-2.476 2.23-4.82 5.455-4.82z" style="color:#fff;fill:#fff;stroke:none;stroke-width:1.93113;-inkscape-stroke:none" transform="scale(.26458)"/>
<g style="font-size:8.48274px;font-family:sans-serif;letter-spacing:0;word-spacing:0;fill:#fff;stroke:none;stroke-width:.525121">
<path d="M62.57 116.77v-1.312l3.28-1.459q.159-.068.306-.102.158-.045.283-.068l.271-.022v-.09q-.136-.012-.271-.046-.125-.023-.283-.057-.147-.045-.306-.113l-3.28-1.459v-1.323l5.068 2.319v1.413z" style="color:#fff;-inkscape-font-specification:&quot;JetBrains Mono, Bold&quot;;fill:#fff;stroke:none;-inkscape-stroke:none" transform="matrix(1.45366 0 0 1.72815 -75.122 -171.953)"/>
<path d="M62.309 110.31v1.903l3.437 1.53.022.007-.022.008-3.437 1.53v1.892l.37-.17 5.221-2.39v-1.75zm.525.817 4.541 2.08v1.076l-4.541 2.078v-.732l3.12-1.389.003-.002a1.56 1.56 0 0 1 .258-.086h.006l.008-.002c.094-.027.176-.047.246-.06l.498-.041v-.574l-.24-.02a1.411 1.411 0 0 1-.231-.04l-.008-.001-.008-.002a9.077 9.077 0 0 1-.263-.053 2.781 2.781 0 0 1-.266-.097l-.004-.002-3.119-1.39z"
style="color:#fff;-inkscape-font-specification:&quot;JetBrains Mono, Bold&quot;;fill:#fff;stroke:none;-inkscape-stroke:none" transform="matrix(1.45366 0 0 1.72815 -75.122 -171.953)"/>
</g>
<g style="font-size:8.48274px;font-family:sans-serif;letter-spacing:0;word-spacing:0;fill:#fff;stroke:none;stroke-width:.525121">
<path d="M69.171 117.754h5.43v1.278h-5.43Z" style="color:#fff;-inkscape-font-specification:&quot;JetBrains Mono, Bold&quot;;fill:#fff;stroke:none;-inkscape-stroke:none" transform="matrix(1.44935 0 0 1.66414 -74.104 -166.906)"/>
<path d="M68.908 117.492v1.802h5.955v-1.802zm.526.524h4.904v.754h-4.904z" style="color:#fff;-inkscape-font-specification:&quot;JetBrains Mono, Bold&quot;;fill:#fff;stroke:none;-inkscape-stroke:none" transform="matrix(1.44935 0 0 1.66414 -74.104 -166.906)"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Tranga</title>
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="styles/index.css">
</head>
<body>
<div id="app"></div>
<script type="module" src="index.jsx"></script>
</body>
</html>

View File

@ -1,7 +0,0 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.tsx';
const domNode = document.getElementById('app');
const root = createRoot(domNode);
root.render(<App />);

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.29289 5.29289C5.68342 4.90237 6.31658 4.90237 6.70711 5.29289L12 10.5858L17.2929 5.29289C17.6834 4.90237 18.3166 4.90237 18.7071 5.29289C19.0976 5.68342 19.0976 6.31658 18.7071 6.70711L13.4142 12L18.7071 17.2929C19.0976 17.6834 19.0976 18.3166 18.7071 18.7071C18.3166 19.0976 17.6834 19.0976 17.2929 18.7071L12 13.4142L6.70711 18.7071C6.31658 19.0976 5.68342 19.0976 5.29289 18.7071C4.90237 18.3166 4.90237 17.6834 5.29289 17.2929L10.5858 12L5.29289 6.70711C4.90237 6.31658 4.90237 5.68342 5.29289 5.29289Z" fill="#0F1729"/>
</svg>

Before

Width:  |  Height:  |  Size: 804 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 440 B

View File

@ -1,43 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns="http://www.w3.org/2000/svg"
height="512pt"
viewBox="0 0 512 512"
width="512pt"
version="1.1"
id="svg4586">
<path
d="m512 256c0 141.386719-114.613281 256-256 256s-256-114.613281-256-256 114.613281-256 256-256 256 114.613281 256 256zm0 0"
fill="#005ed3"
id="path4556"/>
<path
d="m 512,256 c 0,-11.71094 -0.80469,-23.23047 -2.32422,-34.52344 L 382.48047,94.28125 320.52344,121.85938 256,56.933594 212.69531,131.30469 129.51953,94.28125 141.86719,178.42187 49.949219,193.81641 114.32031,256 l -64.371091,62.18359 82.121091,82.16016 -2.55078,17.375 91.95703,91.95703 C 232.76953,511.19531 244.28906,512 256,512 397.38672,512 512,397.38672 512,256 Z"
id="path4558"
style="fill:#00459f"/>
<path
d="m256 86.742188 37.109375 63.738281 70.574219-31.414063-10.527344 71.71875 77.078125 12.910156-54.144531 52.304688 54.144531 52.304688-77.078125 12.910156 10.527344 71.71875-70.574219-31.414063-37.109375 63.738281-37.109375-63.738281-70.574219 31.414063 10.527344-71.71875-77.078125-12.910156 54.144531-52.304688-54.144531-52.304688 77.078125-12.910156-10.527344-71.71875 70.574219 31.414063zm0 0"
fill="#ff0335"
id="path4560"/>
<path
d="m430.230469 308.300781-77.070313 12.910157 10.519532 71.71875-70.570313-31.410157-37.109375 63.742188v-338.523438l37.109375 63.742188 70.570313-31.410157-6.757813 46.101563-3.761719 25.617187 58.800782 9.851563 18.269531 3.058594-13.390625 12.929687-40.75 39.371094 11.378906 10.988281zm0 0"
fill="#c2001b"
id="path4562"/>
<path
d="m256 455.066406-43.304688-74.371094-83.175781 37.023438 12.347657-84.140625-91.917969-15.394531 64.371093-62.183594-64.371093-62.183594 91.917969-15.394531-12.347657-84.140625 83.179688 37.023438 43.300781-74.371094 43.304688 74.371094 83.175781-37.023438-12.347657 84.140625 91.917969 15.394531-64.371093 62.183594 64.371093 62.183594-91.917969 15.398437 12.347657 84.136719-83.175781-37.023438zm-30.917969-112.722656 30.917969 53.101562 30.917969-53.101562 57.964843 25.800781-8.703124-59.292969 62.238281-10.425781-43.917969-42.425781 43.917969-42.425781-62.238281-10.425781 8.703124-59.292969-57.964843 25.800781-30.917969-53.101562-30.917969 53.101562-57.964843-25.800781 8.703124 59.292969-62.238281 10.425781 43.917969 42.425781-43.917969 42.425781 62.238281 10.425781-8.703124 59.292969zm0 0"
fill="#ffdf47"
id="path4564"/>
<path
d="m403.308594 261.441406-5.628906-5.441406 25.160156-24.300781 39.210937-37.878907-55.75-9.339843-36.171875-6.058594 2.800782-19.09375 9.550781-65.046875-83.179688 37.019531-43.300781-74.371093v59.621093l30.921875 53.109375 57.957031-25.808594-3.910156 26.667969-2.546875 17.378907-2.242187 15.25 2.480468.421874 59.761719 10.007813-43.921875 42.421875 16.96875 16.390625 26.953125 26.03125-62.242187 10.429687 8.699218 59.296876-57.957031-25.808594-30.921875 53.109375v59.621093l43.300781-74.371093 83.179688 37.019531-12.351563-84.140625 91.921875-15.398437zm0 0"
fill="#fec000"
id="path4566"/>
<g
aria-label="K"
transform="matrix(1.1590846,-0.34467221,0.22789693,0.794981,0,0)"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:296.55969238px;line-height:125%;font-family:Impact;-inkscape-font-specification:Impact;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.54528999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="text4596">
<path
d="m 220.91497,266.9035 -34.89789,105.85211 38.2284,128.58643 H 161.2555 L 136.63873,400.84769 V 501.34204 H 75.676021 V 266.9035 h 60.962709 v 91.08205 l 27.07845,-91.08205 z"
style="font-size:296.55969238px;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.54528999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path824"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -1,40 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="50mm" height="50mm" viewBox="0 0 50 50">
<defs>
<linearGradient id="b">
<stop offset="0" style="stop-color:#348878;stop-opacity:1"/>
<stop offset="1" style="stop-color:#52bca6;stop-opacity:1"/>
</linearGradient>
<linearGradient id="a">
<stop offset="0" style="stop-color:#348878;stop-opacity:1"/>
<stop offset="1" style="stop-color:#56bda8;stop-opacity:1"/>
</linearGradient>
<linearGradient xlink:href="#a" id="e" x1="160.722" x2="168.412" y1="128.533" y2="134.326" gradientTransform="matrix(3.74959 0 0 3.74959 -541.79 -387.599)" gradientUnits="userSpaceOnUse"/>
<linearGradient xlink:href="#b" id="c" x1=".034" x2="50.319" y1="0" y2="50.285" gradientTransform="matrix(.99434 0 0 .99434 -.034 0)" gradientUnits="userSpaceOnUse"/>
<filter id="d" width="1.176" height="1.211" x="-.076" y="-.092" style="color-interpolation-filters:sRGB">
<feFlood flood-color="#fff" flood-opacity=".192" result="flood"/>
<feComposite in="flood" in2="SourceGraphic" operator="in" result="composite1"/>
<feGaussianBlur in="composite1" result="blur" stdDeviation="4"/>
<feOffset dx="3" dy="2.954" result="offset"/>
<feComposite in="SourceGraphic" in2="offset" result="composite2"/>
</filter>
</defs>
<g style="display:inline">
<path d="M0 0h50v50H0z" style="fill:url(#c);fill-opacity:1;stroke:none;stroke-width:.286502;stroke-linejoin:bevel"/>
</g>
<g style="display:inline">
<path d="M50.4 46.883c-9.168 0-17.023 7.214-17.023 16.387v.007l.09 71.37-2.303 16.992 31.313-8.319h77.841c9.17 0 17.024-7.224 17.024-16.396V63.27c0-9.17-7.85-16.383-17.016-16.387h-.008zm0 11.566h89.926c3.222.004 5.45 2.347 5.45 4.82v63.655c0 2.475-2.232 4.82-5.457 4.82h-79.54l-15.908 4.807.162-.938-.088-72.343c0-2.476 2.23-4.82 5.455-4.82z" style="color:#fff;display:inline;fill:#fff;stroke:none;stroke-width:1.93113;-inkscape-stroke:none;filter:url(#d)" transform="scale(.26458)"/>
</g>
<g style="display:inline">
<path d="M88.2 95.309H64.92c-1.601 0-2.91 1.236-2.91 2.746l.022 18.602-.435 2.506 6.231-1.881H88.2c1.6 0 2.91-1.236 2.91-2.747v-16.48c0-1.51-1.31-2.746-2.91-2.746z" style="color:#fff;fill:url(#e);stroke:none;stroke-width:2.49558;-inkscape-stroke:none" transform="translate(-51.147 -81.516)"/>
<path d="M50.4 46.883c-9.168 0-17.023 7.214-17.023 16.387v.007l.09 71.37-2.303 16.992 31.313-8.319h77.841c9.17 0 17.024-7.224 17.024-16.396V63.27c0-9.17-7.85-16.383-17.016-16.387h-.008zm0 11.566h89.926c3.222.004 5.45 2.347 5.45 4.82v63.655c0 2.475-2.232 4.82-5.457 4.82h-79.54l-15.908 4.807.162-.938-.088-72.343c0-2.476 2.23-4.82 5.455-4.82z" style="color:#fff;fill:#fff;stroke:none;stroke-width:1.93113;-inkscape-stroke:none" transform="scale(.26458)"/>
<g style="font-size:8.48274px;font-family:sans-serif;letter-spacing:0;word-spacing:0;fill:#fff;stroke:none;stroke-width:.525121">
<path d="M62.57 116.77v-1.312l3.28-1.459q.159-.068.306-.102.158-.045.283-.068l.271-.022v-.09q-.136-.012-.271-.046-.125-.023-.283-.057-.147-.045-.306-.113l-3.28-1.459v-1.323l5.068 2.319v1.413z" style="color:#fff;-inkscape-font-specification:&quot;JetBrains Mono, Bold&quot;;fill:#fff;stroke:none;-inkscape-stroke:none" transform="matrix(1.45366 0 0 1.72815 -75.122 -171.953)"/>
<path d="M62.309 110.31v1.903l3.437 1.53.022.007-.022.008-3.437 1.53v1.892l.37-.17 5.221-2.39v-1.75zm.525.817 4.541 2.08v1.076l-4.541 2.078v-.732l3.12-1.389.003-.002a1.56 1.56 0 0 1 .258-.086h.006l.008-.002c.094-.027.176-.047.246-.06l.498-.041v-.574l-.24-.02a1.411 1.411 0 0 1-.231-.04l-.008-.001-.008-.002a9.077 9.077 0 0 1-.263-.053 2.781 2.781 0 0 1-.266-.097l-.004-.002-3.119-1.39z"
style="color:#fff;-inkscape-font-specification:&quot;JetBrains Mono, Bold&quot;;fill:#fff;stroke:none;-inkscape-stroke:none" transform="matrix(1.45366 0 0 1.72815 -75.122 -171.953)"/>
</g>
<g style="font-size:8.48274px;font-family:sans-serif;letter-spacing:0;word-spacing:0;fill:#fff;stroke:none;stroke-width:.525121">
<path d="M69.171 117.754h5.43v1.278h-5.43Z" style="color:#fff;-inkscape-font-specification:&quot;JetBrains Mono, Bold&quot;;fill:#fff;stroke:none;-inkscape-stroke:none" transform="matrix(1.44935 0 0 1.66414 -74.104 -166.906)"/>
<path d="M68.908 117.492v1.802h5.955v-1.802zm.526.524h4.904v.754h-4.904z" style="color:#fff;-inkscape-font-specification:&quot;JetBrains Mono, Bold&quot;;fill:#fff;stroke:none;-inkscape-stroke:none" transform="matrix(1.44935 0 0 1.66414 -74.104 -166.906)"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg fill="#000000" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="800px" height="800px" viewBox="0 0 971.986 971.986"
xml:space="preserve">
<g>
<path d="M370.216,459.3c10.2,11.1,15.8,25.6,15.8,40.6v442c0,26.601,32.1,40.101,51.1,21.4l123.3-141.3
c16.5-19.8,25.6-29.601,25.6-49.2V500c0-15,5.7-29.5,15.8-40.601L955.615,75.5c26.5-28.8,6.101-75.5-33.1-75.5h-873
c-39.2,0-59.7,46.6-33.1,75.5L370.216,459.3z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 714 B

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
<title>
external link
</title>
<path fill="#fff" d="M6 1h5v5L8.86 3.85 4.7 8 4 7.3l4.15-4.16L6 1Z M2 3h2v1H2v6h6V8h1v2a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 304 B

View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg fill="#000000" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="800px" height="800px" viewBox="0 0 93.5 93.5" xml:space="preserve">
<g>
<g>
<path d="M93.5,40.899c0-2.453-1.995-4.447-4.448-4.447H81.98c-0.74-2.545-1.756-5.001-3.035-7.331l4.998-5
c0.826-0.827,1.303-1.973,1.303-3.146c0-1.19-0.462-2.306-1.303-3.146L75.67,9.555c-1.613-1.615-4.673-1.618-6.29,0l-5,5
c-2.327-1.28-4.786-2.296-7.332-3.037v-7.07C57.048,1.995,55.053,0,52.602,0H40.899c-2.453,0-4.447,1.995-4.447,4.448v7.071
c-2.546,0.741-5.005,1.757-7.333,3.037l-5-5c-1.68-1.679-4.609-1.679-6.288,0L9.555,17.83c-1.734,1.734-1.734,4.555,0,6.289
l4.999,5c-1.279,2.33-2.295,4.788-3.036,7.333h-7.07C1.995,36.452,0,38.447,0,40.899V52.6c0,2.453,1.995,4.447,4.448,4.447h7.071
c0.74,2.545,1.757,5.003,3.036,7.332l-4.998,4.999c-0.827,0.827-1.303,1.974-1.303,3.146c0,1.189,0.462,2.307,1.302,3.146
l8.274,8.273c1.614,1.615,4.674,1.619,6.29,0l5-5c2.328,1.279,4.786,2.297,7.333,3.037v7.071c0,2.453,1.995,4.448,4.447,4.448
h11.702c2.453,0,4.446-1.995,4.446-4.448V81.98c2.546-0.74,5.005-1.756,7.332-3.037l5,5c1.681,1.68,4.608,1.68,6.288,0
l8.275-8.273c1.734-1.734,1.734-4.555,0-6.289l-4.998-5.001c1.279-2.329,2.295-4.787,3.035-7.332h7.071
c2.453,0,4.448-1.995,4.448-4.446V40.899z M62.947,46.75c0,8.932-7.266,16.197-16.197,16.197c-8.931,0-16.197-7.266-16.197-16.197
c0-8.931,7.266-16.197,16.197-16.197C55.682,30.553,62.947,37.819,62.947,46.75z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,19 +0,0 @@
import {getData} from "../App";
import IChapter from "./interfaces/IChapter";
export default class ChapterFunctions {
static async GetChapterFromId(apiUri: string, chapterId: string): Promise<IChapter> {
if(chapterId === undefined || chapterId === null) {
console.error(`chapterId was not provided`);
return Promise.reject();
}
return getData(`${apiUri}/v2/Query/Chapter/${chapterId}`)
.then((json) => {
//console.info("Got all MangaFunctions");
const ret = json as IChapter;
//console.debug(ret);
return (ret);
});
}
}

View File

@ -1,51 +0,0 @@
import React, {useEffect} from 'react';
import '../styles/footer.css';
import JobFunctions from './JobFunctions';
import Icon from '@mdi/react';
import {mdiCounter, mdiEyeCheck, mdiRun, mdiTrayFull} from '@mdi/js';
import QueuePopUp from "./QueuePopUp";
import {JobState, JobType} from "./interfaces/Jobs/IJob";
export default function Footer({connectedToBackend, apiUri, checkConnectedInterval} : {connectedToBackend: boolean, apiUri: string, checkConnectedInterval: number}) {
const [MonitoringJobsCount, setMonitoringJobsCount] = React.useState(0);
const [AllJobsCount, setAllJobsCount] = React.useState(0);
const [RunningJobsCount, setRunningJobsCount] = React.useState(0);
const [WaitingJobsCount, setWaitingJobs] = React.useState(0);
const [countUpdateInterval, setCountUpdateInterval] = React.useState<number | undefined>(undefined);
function UpdateBackendState(){
JobFunctions.GetJobsWithType(apiUri, JobType.DownloadAvailableChaptersJob).then((jobs) => setMonitoringJobsCount(jobs.length));
JobFunctions.GetAllJobs(apiUri).then((jobs) => setAllJobsCount(jobs.length));
JobFunctions.GetJobsInState(apiUri, JobState.Running).then((jobs) => setRunningJobsCount(jobs.length));
JobFunctions.GetJobsInState(apiUri, JobState.Waiting).then((jobs) => setWaitingJobs(jobs.length));
}
useEffect(() => {
if(connectedToBackend){
UpdateBackendState();
if(countUpdateInterval === undefined){
setCountUpdateInterval(setInterval(() => {
UpdateBackendState();
}, checkConnectedInterval));
}
}else{
clearInterval(countUpdateInterval);
setCountUpdateInterval(undefined);
}
}, [connectedToBackend]);
return (
<footer>
<div className="statusBadge" ><Icon path={mdiEyeCheck} size={1}/> <span>{MonitoringJobsCount}</span></div>
<span>+</span>
<QueuePopUp connectedToBackend={connectedToBackend} apiUri={apiUri} checkConnectedInterval={checkConnectedInterval}>
<div className="statusBadge hoverHand"><Icon path={mdiRun} size={1}/> <span>{RunningJobsCount}</span>
</div>
<span>+</span>
<div className="statusBadge hoverHand"><Icon path={mdiTrayFull} size={1}/><span>{WaitingJobsCount}</span></div>
</QueuePopUp>
<span>=</span>
<div className="statusBadge"><Icon path={mdiCounter} size={1}/> <span>{AllJobsCount}</span></div>
<p id="madeWith">Made with Blåhaj 🦈</p>
</footer>)
}

View File

@ -1,15 +0,0 @@
import React from 'react';
import '../styles/header.css'
import IFrontendSettings from "./interfaces/IFrontendSettings";
import Settings from "./Settings";
export default function Header({backendConnected, apiUri, settings, setFrontendSettings} : {backendConnected: boolean, apiUri: string, settings: IFrontendSettings, setFrontendSettings: (settings: IFrontendSettings) => void}){
return (
<header>
<div id="titlebox">
<img alt="website image is Blahaj" src="../media/blahaj.png"/>
<span>Tranga</span>
</div>
<Settings backendConnected={backendConnected} apiUri={apiUri} frontendSettings={settings} setFrontendSettings={setFrontendSettings} />
</header>)
}

View File

@ -1,208 +0,0 @@
import {deleteData, getData, patchData, postData, putData} from '../App';
import IJob, {JobState, JobType} from "./interfaces/Jobs/IJob";
import IModifyJobRecord from "./interfaces/records/IModifyJobRecord";
import IDownloadAvailableJobsRecord from "./interfaces/records/IDownloadAvailableJobsRecord";
export default class JobFunctions
{
static IntervalStringFromDate(date: Date) : string {
let x = new Date(date);
return `${x.getDay()}.${x.getHours()}:${x.getMinutes()}:${x.getSeconds()}`;
}
static async GetAllJobs(apiUri: string): Promise<IJob[]> {
//console.info("Getting all Jobs");
return getData(`${apiUri}/v2/Job`)
.then((json) => {
//console.info("Got all Jobs");
const ret = json as IJob[];
//console.debug(ret);
return (ret);
});
}
static async GetJobsWithIds(apiUri: string, jobIds: string[]): Promise<IJob[]> {
return postData(`${apiUri}/v2/Job/WithIDs`, jobIds)
.then((json) => {
//console.info("Got all Jobs");
const ret = json as IJob[];
//console.debug(ret);
return (ret);
});
}
static async GetJobsInState(apiUri: string, state: JobState): Promise<IJob[]> {
if(state == null || state == undefined) {
console.error(`state was not provided`);
return Promise.reject();
}
return getData(`${apiUri}/v2/Job/State/${state}`)
.then((json) => {
//console.info("Got all Jobs");
const ret = json as IJob[];
//console.debug(ret);
return (ret);
});
}
static async GetJobsWithType(apiUri: string, jobType: JobType): Promise<IJob[]> {
if(jobType == null || jobType == undefined) {
console.error(`jobType was not provided`);
return Promise.reject();
}
return getData(`${apiUri}/v2/Job/Type/${jobType}`)
.then((json) => {
//console.info("Got all Jobs");
const ret = json as IJob[];
//console.debug(ret);
return (ret);
});
}
static async GetJobsOfTypeAndWithState(apiUri: string, jobType: JobType, state: JobState): Promise<IJob[]> {
if(jobType == null || jobType == undefined) {
console.error(`jobType was not provided`);
return Promise.reject();
}
if(state == null || state == undefined) {
console.error(`state was not provided`);
return Promise.reject();
}
return getData(`${apiUri}/v2/Job/TypeAndState/${jobType}/${state}`)
.then((json) => {
//console.info("Got all Jobs");
const ret = json as IJob[];
//console.debug(ret);
return (ret);
});
}
static async GetJob(apiUri: string, jobId: string): Promise<IJob>{
if(jobId === undefined || jobId === null || jobId.length < 1) {
console.error(`JobId was not provided`);
return Promise.reject();
}
//console.info(`Getting JobFunctions ${jobId}`);
return getData(`${apiUri}/v2/Job/${jobId}`)
.then((json) => {
//console.info(`Got JobFunctions ${jobId}`);
const ret = json as IJob;
//console.debug(ret);
return (ret);
});
}
static DeleteJob(apiUri: string, jobId: string) : Promise<void> {
if(jobId === undefined || jobId === null || jobId.length < 1) {
console.error(`JobId was not provided`);
return Promise.reject();
}
return deleteData(`${apiUri}/v2/Job/${jobId}`);
}
static async ModifyJob(apiUri: string, jobId: string, modifyData: IModifyJobRecord): Promise<IJob> {
if(jobId === undefined || jobId === null || jobId.length < 1) {
console.error(`JobId was not provided`);
return Promise.reject();
}
if(modifyData === undefined || modifyData === null) {
console.error(`modifyData was not provided`);
return Promise.reject();
}
return patchData(`${apiUri}/v2/Job/${jobId}`, modifyData)
.then((json) => {
//console.info(`Got JobFunctions ${jobId}`);
const ret = json as IJob;
//console.debug(ret);
return (ret);
});
}
static async CreateDownloadAvailableChaptersJob(apiUri: string, mangaId: string, data: IDownloadAvailableJobsRecord): Promise<string[]> {
if(mangaId === undefined || mangaId === null || mangaId.length < 1) {
console.error(`mangaId was not provided`);
return Promise.reject();
}
if(data === undefined || data === null) {
console.error(`recurrenceMs was not provided`);
return Promise.reject();
}
return putData(`${apiUri}/v2/Job/DownloadAvailableChaptersJob/${mangaId}`, data)
.then((json) => {
//console.info(`Got JobFunctions ${jobId}`);
const ret = json as string[];
//console.debug(ret);
return (ret);
});
}
static async CreateDownloadSingleChapterJob(apiUri: string, chapterId: string): Promise<string[]> {
if(chapterId === undefined || chapterId === null || chapterId.length < 1) {
console.error(`mangaId was not provided`);
return Promise.reject();
}
return putData(`${apiUri}/v2/Job/DownloadSingleChapterJob/${chapterId}`, {})
.then((json) => {
//console.info(`Got JobFunctions ${jobId}`);
const ret = json as string[];
//console.debug(ret);
return (ret);
});
}
static async CreateUpdateFilesJob(apiUri: string, mangaId: string): Promise<string[]> {
if(mangaId === undefined || mangaId === null || mangaId.length < 1) {
console.error(`mangaId was not provided`);
return Promise.reject();
}
return putData(`${apiUri}/v2/Job/UpdateFilesJob/${mangaId}`, {})
.then((json) => {
//console.info(`Got JobFunctions ${jobId}`);
const ret = json as string[];
//console.debug(ret);
return (ret);
});
}
static async CreateUpdateAllFilesJob(apiUri: string): Promise<string[]> {
return putData(`${apiUri}/v2/Job/UpdateAllFilesJob`, {})
.then((json) => {
//console.info(`Got JobFunctions ${jobId}`);
const ret = json as string[];
//console.debug(ret);
return (ret);
});
}
static async CreateUpdateMetadataJob(apiUri: string, mangaId: string): Promise<string[]> {
if(mangaId === undefined || mangaId === null || mangaId.length < 1) {
console.error(`mangaId was not provided`);
return Promise.reject();
}
return putData(`${apiUri}/v2/Job/UpdateMetadataJob/${mangaId}`, {})
.then((json) => {
//console.info(`Got JobFunctions ${jobId}`);
const ret = json as string[];
//console.debug(ret);
return (ret);
});
}
static async CreateUpdateAllMetadataJob(apiUri: string): Promise<string[]> {
return putData(`${apiUri}/v2/Job/UpdateAllMetadataJob`, {})
.then((json) => {
//console.info(`Got JobFunctions ${jobId}`);
const ret = json as string[];
//console.debug(ret);
return (ret);
});
}
static StartJob(apiUri: string, jobId: string) : Promise<object> {
return postData(`${apiUri}/v2/Job/${jobId}/Start`, {});
}
static StopJob(apiUri: string, jobId: string) : Promise<object> {
return postData(`${apiUri}/v2/Job/${jobId}/Stop`, {});
}
}

View File

@ -1,6 +0,0 @@
import "../styles/loader.css";
import {CSSProperties} from "react";
export default function Loader({loading, style} : {loading: boolean, style?:CSSProperties|null}) {
return <span is-loading={loading ? "loading" : "done"} style={style ? style : undefined}></span>
}

View File

@ -1,50 +0,0 @@
import ILocalLibrary from "./interfaces/ILocalLibrary";
import {deleteData, getData, patchData, putData} from "../App";
import INewLibraryRecord from "./interfaces/records/INewLibraryRecord";
export default class LocalLibraryFunctions
{
static async GetLibraries(apiUri: string): Promise<ILocalLibrary[]> {
return getData(`${apiUri}/v2/LocalLibraries`)
.then((json) => {
const ret = json as ILocalLibrary[];
return (ret);
});
}
static async GetLibrary(apiUri: string, libraryId: string): Promise<ILocalLibrary> {
return getData(`${apiUri}/v2/LocalLibraries/${libraryId}`)
.then((json) => {
const ret = json as ILocalLibrary;
//console.debug(ret);
return (ret);
});
}
static async CreateLibrary(apiUri: string, data: INewLibraryRecord): Promise<ILocalLibrary> {
return putData(`${apiUri}/v2/LocalLibraries`, data)
.then((json) => {
const ret = json as ILocalLibrary;
//console.debug(ret);
return (ret);
});
}
static async DeleteLibrary(apiUri: string, libraryId: string): Promise<void> {
return deleteData(`${apiUri}/v2/LocalLibraries/${libraryId}`);
}
static async ChangeLibraryPath(apiUri: string, libraryId: string, newPath: string): Promise<void> {
return patchData(`${apiUri}/v2/LocalLibraries/${libraryId}/ChangeBasePath`, newPath)
.then(()=> {
return Promise.resolve()
});
}
static async ChangeLibraryName(apiUri: string, libraryId: string, newName: string): Promise<void> {
return patchData(`${apiUri}/v2/LocalLibraries/${libraryId}/ChangeName`, newName)
.then(()=> {
return Promise.resolve()
});
}
}

View File

@ -1,44 +0,0 @@
import IMangaConnector from './interfaces/IMangaConnector';
import {getData, patchData} from '../App';
export class MangaConnectorFunctions
{
static async GetAllConnectors(apiUri: string): Promise<IMangaConnector[]> {
//console.info("Getting all MangaConnectors");
return getData(`${apiUri}/v2/MangaConnector`)
.then((json) => {
//console.info("Got all MangaConnectors");
return (json as IMangaConnector[]);
});
}
static async GetEnabledConnectors(apiUri: string): Promise<IMangaConnector[]> {
//console.info("Getting all enabled MangaConnectors");
return getData(`${apiUri}/v2/MangaConnector/enabled`)
.then((json) => {
//console.info("Got all enabled MangaConnectors");
return (json as IMangaConnector[]);
});
}
static async GetDisabledConnectors(apiUri: string): Promise<IMangaConnector[]> {
//console.info("Getting all disabled MangaConnectors");
return getData(`${apiUri}/v2/MangaConnector/disabled`)
.then((json) => {
//console.info("Got all disabled MangaConnectors");
return (json as IMangaConnector[]);
});
}
static async SetConnectorEnabled(apiUri: string, connectorName: string, enabled: boolean): Promise<object> {
if(connectorName === undefined || connectorName === null || connectorName.length < 1) {
console.error(`connectorName was not provided`);
return Promise.reject();
}
if(enabled === undefined || enabled === null) {
console.error(`enabled was not provided`);
return Promise.reject();
}
return patchData(`${apiUri}/v2/MangaConnector/${connectorName}/SetEnabled/${enabled}`, {});
}
}

View File

@ -1,156 +0,0 @@
import IManga from './interfaces/IManga';
import {deleteData, getData, patchData, postData} from '../App';
import IChapter from "./interfaces/IChapter";
export default class MangaFunctions
{
static async GetAllManga(apiUri: string): Promise<IManga[]> {
//console.info("Getting all MangaFunctions");
return getData(`${apiUri}/v2/Manga`)
.then((json) => {
//console.info("Got all MangaFunctions");
const ret = json as IManga[];
//console.debug(ret);
return (ret);
});
}
static async GetMangaWithIds(apiUri: string, mangaIds: string[]): Promise<IManga[]> {
if(mangaIds === undefined || mangaIds === null || mangaIds.length < 1) {
console.error(`mangaIds was not provided`);
return Promise.reject();
}
//console.debug(`Getting Mangas ${internalIds.join(",")}`);
return await postData(`${apiUri}/v2/Manga/WithIds`, mangaIds)
.then((json) => {
//console.debug(`Got MangaFunctions ${internalIds.join(",")}`);
const ret = json as IManga[];
//console.debug(ret);
return (ret);
});
}
static async GetMangaById(apiUri: string, mangaId: string): Promise<IManga> {
if(mangaId === undefined || mangaId === null || mangaId.length < 1) {
console.error(`mangaId was not provided`);
return Promise.reject();
}
//console.info(`Getting MangaFunctions ${internalId}`);
return await getData(`${apiUri}/v2/Manga/${mangaId}`)
.then((json) => {
//console.info(`Got MangaFunctions ${internalId}`);
const ret = json as IManga;
//console.debug(ret);
return (ret);
});
}
static async DeleteManga(apiUri: string, mangaId: string): Promise<void> {
if(mangaId === undefined || mangaId === null || mangaId.length < 1) {
console.error(`mangaId was not provided`);
return Promise.reject();
}
return deleteData(`${apiUri}/v2/Manga/${mangaId}`);
}
static GetMangaCoverImageUrl(apiUri: string, mangaId: string, ref: HTMLImageElement | undefined): string {
//console.debug(`Getting MangaFunctions Cover-Url ${internalId}`);
if(ref == null || ref == undefined)
return `${apiUri}/v2/Manga/${mangaId}/Cover?width=64&height=64`;
return `${apiUri}/v2/Manga/${mangaId}/Cover?width=${ref.clientWidth}&height=${ref.clientHeight}`;
}
static async GetChapters(apiUri: string, mangaId: string): Promise<IChapter[]> {
if(mangaId === undefined || mangaId === null || mangaId.length < 1) {
console.error(`mangaId was not provided`);
return Promise.reject();
}
return getData(`${apiUri}/v2/Manga/${mangaId}/Chapters`)
.then((json) => {
//console.info(`Got MangaFunctions ${internalId}`);
const ret = json as IChapter[];
//console.debug(ret);
return (ret);
});
}
static async GetDownloadedChapters(apiUri: string, mangaId: string): Promise<IChapter[]> {
if(mangaId === undefined || mangaId === null || mangaId.length < 1) {
console.error(`mangaId was not provided`);
return Promise.reject();
}
return getData(`${apiUri}/v2/Manga/${mangaId}/Chapters/Downloaded`)
.then((json) => {
//console.info(`Got MangaFunctions ${internalId}`);
const ret = json as IChapter[];
//console.debug(ret);
return (ret);
});
}
static async GetNotDownloadedChapters(apiUri: string, mangaId: string): Promise<IChapter[]> {
if(mangaId === undefined || mangaId === null || mangaId.length < 1) {
console.error(`mangaId was not provided`);
return Promise.reject();
}
return getData(`${apiUri}/v2/Manga/${mangaId}/Chapters/NotDownloaded`)
.then((json) => {
//console.info(`Got MangaFunctions ${internalId}`);
const ret = json as IChapter[];
//console.debug(ret);
return (ret);
});
}
static async GetLatestChapterAvailable(apiUri: string, mangaId: string): Promise<IChapter> {
if(mangaId === undefined || mangaId === null || mangaId.length < 1) {
console.error(`mangaId was not provided`);
return Promise.reject();
}
return getData(`${apiUri}/v2/Manga/${mangaId}/Chapter/LatestAvailable`)
.then((json) => {
//console.info(`Got MangaFunctions ${internalId}`);
const ret = json as IChapter;
//console.debug(ret);
return (ret);
});
}
static async GetLatestChapterDownloaded(apiUri: string, mangaId: string): Promise<IChapter> {
if(mangaId === undefined || mangaId === null || mangaId.length < 1) {
console.error(`mangaId was not provided`);
return Promise.reject();
}
return getData(`${apiUri}/v2/Manga/${mangaId}/Chapter/LatestDownloaded`)
.then((json) => {
//console.info(`Got MangaFunctions ${internalId}`);
const ret = json as IChapter;
//console.debug(ret);
return (ret);
});
}
static async SetIgnoreThreshold(apiUri: string, mangaId: string, chapterThreshold: number): Promise<object> {
if(mangaId === undefined || mangaId === null || mangaId.length < 1) {
console.error(`mangaId was not provided`);
return Promise.reject();
}
if(chapterThreshold === undefined || chapterThreshold === null) {
console.error(`chapterThreshold was not provided`);
return Promise.reject();
}
return patchData(`${apiUri}/v2/Manga/${mangaId}/IgnoreChaptersBefore`, chapterThreshold);
}
static async MoveFolder(apiUri: string, mangaId: string, newPath: string): Promise<object> {
if(mangaId === undefined || mangaId === null || mangaId.length < 1) {
console.error(`mangaId was not provided`);
return Promise.reject();
}
if(newPath === undefined || newPath === null || newPath.length < 1) {
console.error(`newPath was not provided`);
return Promise.reject();
}
return postData(`${apiUri}/v2/Manga/{MangaId}/MoveFolder`, {newPath});
}
}

View File

@ -1,65 +0,0 @@
import React, {EventHandler, ReactElement, useEffect, useState} from 'react';
import JobFunctions from './JobFunctions';
import '../styles/monitorMangaList.css';
import {JobType} from "./interfaces/Jobs/IJob";
import '../styles/mangaCover.css'
import IDownloadAvailableChaptersJob from "./interfaces/Jobs/IDownloadAvailableChaptersJob";
import {MangaItem} from "./interfaces/IManga";
import MangaFunctions from "./MangaFunctions";
export default function MonitorJobsList({onStartSearch, connectedToBackend, apiUri, checkConnectedInterval} : {onStartSearch() : void, connectedToBackend: boolean, apiUri: string, checkConnectedInterval: number}) {
const [MonitoringJobs, setMonitoringJobs] = useState<IDownloadAvailableChaptersJob[]>([]);
const [joblistUpdateInterval, setJoblistUpdateInterval] = React.useState<number | undefined>(undefined);
useEffect(() => {
if(connectedToBackend) {
UpdateMonitoringJobsList();
if(joblistUpdateInterval === undefined){
setJoblistUpdateInterval(setInterval(() => {
UpdateMonitoringJobsList();
}, checkConnectedInterval));
}
}else{
clearInterval(joblistUpdateInterval);
setJoblistUpdateInterval(undefined);
}
}, [connectedToBackend]);
function UpdateMonitoringJobsList(){
if(!connectedToBackend)
return;
//console.debug("Updating MonitoringJobsList");
JobFunctions.GetJobsWithType(apiUri, JobType.DownloadAvailableChaptersJob)
.then((jobs) => jobs as IDownloadAvailableChaptersJob[])
.then((jobs) => {
if(jobs.length != MonitoringJobs.length ||
MonitoringJobs.filter(j => jobs.find(nj => nj.jobId == j.jobId)).length > 1 ||
jobs.filter(nj => MonitoringJobs.find(j => nj.jobId == j.jobId)).length > 1){
setMonitoringJobs(jobs);
}
});
}
function StartSearchMangaEntry() : ReactElement {
return (<div key="monitorMangaEntry.StartSearch" className="startSearchEntry MangaItem" onClick={onStartSearch}>
<img className="MangaItem-Cover" src="../media/blahaj.png" alt="Blahaj"></img>
<div>
<div style={{margin: "30px auto", color: "black", textShadow: "1px 2px #f5a9b8"}} className="MangaItem-Name">Add new Manga</div>
<div style={{fontSize: "42pt", textAlign: "center", textShadow: "1px 2px #5bcefa"}}>+</div>
</div>
</div>);
}
return (
<div id="MonitorMangaList">
<StartSearchMangaEntry />
{MonitoringJobs.map((MonitoringJob) =>
<MangaItem apiUri={apiUri} mangaId={MonitoringJob.mangaId} key={MonitoringJob.mangaId}>
<></>
<button className="Manga-DeleteButton" onClick={() => {
MangaFunctions.DeleteManga(apiUri, MonitoringJob.mangaId);
}}>Delete</button>
</MangaItem>
)}
</div>);
}

View File

@ -1,98 +0,0 @@
import INotificationConnector from "./interfaces/INotificationConnector";
import {deleteData, getData, putData} from "../App";
import IGotifyRecord from "./interfaces/records/IGotifyRecord";
import INtfyRecord from "./interfaces/records/INtfyRecord";
import ILunaseaRecord from "./interfaces/records/ILunaseaRecord";
export default class NotificationConnectorFunctions {
static async GetNotificationConnectors(apiUri: string) : Promise<INotificationConnector[]> {
//console.info("Getting Notification Connectors");
return getData(`${apiUri}/v2/NotificationConnector`)
.then((json) => {
//console.info("Got Notification Connectors");
const ret = json as INotificationConnector[];
//console.debug(ret);
return (ret);
});
}
static async CreateNotificationConnector(apiUri: string, newConnector: INotificationConnector): Promise<string> {
return putData(`${apiUri}/v2/NotificationConnector`, newConnector)
.then((json) => {
//console.info("Got Notification Connectors");
const ret = json as unknown as string;
//console.debug(ret);
return (ret);
});
}
static async GetNotificationConnectorWithId(apiUri: string, notificationConnectorId: string) : Promise<INotificationConnector> {
if(notificationConnectorId === undefined || notificationConnectorId === null || notificationConnectorId.length < 1) {
console.error(`notificationConnectorId was not provided`);
return Promise.reject();
}
//console.info("Getting Notification Connectors");
return getData(`${apiUri}/v2/NotificationConnector/${notificationConnectorId}`)
.then((json) => {
//console.info("Got Notification Connectors");
const ret = json as INotificationConnector;
//console.debug(ret);
return (ret);
});
}
static async DeleteNotificationConnectorWithId(apiUri: string, notificationConnectorId: string) : Promise<void> {
if(notificationConnectorId === undefined || notificationConnectorId === null || notificationConnectorId.length < 1) {
console.error(`notificationConnectorId was not provided`);
return Promise.reject();
}
//console.info("Getting Notification Connectors");
return deleteData(`${apiUri}/v2/NotificationConnector/${notificationConnectorId}`);
}
static async CreateGotify(apiUri: string, gotify: IGotifyRecord) : Promise<string> {
if(gotify === undefined || gotify === null) {
console.error(`gotify was not provided`);
return Promise.reject();
}
//console.info("Getting Notification Connectors");
return putData(`${apiUri}/v2/NotificationConnector/Gotify`, gotify)
.then((json) => {
//console.info("Got Notification Connectors");
const ret = json as unknown as string;
//console.debug(ret);
return (ret);
});
}
static async CreateNtfy(apiUri: string, ntfy: INtfyRecord) : Promise<string> {
if(ntfy === undefined || ntfy === null) {
console.error(`ntfy was not provided`);
return Promise.reject();
}
//console.info("Getting Notification Connectors");
return putData(`${apiUri}/v2/NotificationConnector/Ntfy`, ntfy)
.then((json) => {
//console.info("Got Notification Connectors");
const ret = json as unknown as string;
//console.debug(ret);
return (ret);
});
}
static async CreateLunasea(apiUri: string, lunasea: ILunaseaRecord) : Promise<string> {
if(lunasea === undefined || lunasea === null) {
console.error(`ntfy was not provided`);
return Promise.reject();
}
//console.info("Getting Notification Connectors");
return putData(`${apiUri}/v2/NotificationConnector/Lunasea`, lunasea)
.then((json) => {
//console.info("Got Notification Connectors");
const ret = json as unknown as string;
//console.debug(ret);
return (ret);
});
}
}

View File

@ -1,75 +0,0 @@
import React, {ReactElement, useEffect, useState} from 'react';
import IJob, {JobState, JobType} from "./interfaces/Jobs/IJob";
import '../styles/queuePopUp.css';
import '../styles/popup.css';
import JobFunctions from "./JobFunctions";
import IDownloadSingleChapterJob from "./interfaces/Jobs/IDownloadSingleChapterJob";
import {ChapterItem} from "./interfaces/IChapter";
export default function QueuePopUp({connectedToBackend, children, apiUri, checkConnectedInterval} : {connectedToBackend: boolean, children: ReactElement[], apiUri: string, checkConnectedInterval: number}) {
const [WaitingJobs, setWaitingJobs] = React.useState<IJob[]>([]);
const [RunningJobs, setRunningJobs] = React.useState<IJob[]>([]);
const [showQueuePopup, setShowQueuePopup] = useState<boolean>(false);
const [queueListInterval, setQueueListInterval] = React.useState<number | undefined>(undefined);
useEffect(() => {
if(connectedToBackend) {
UpdateMonitoringJobsList();
if(queueListInterval === undefined){
setQueueListInterval(setInterval(() => {
UpdateMonitoringJobsList();
}, checkConnectedInterval));
}
}else{
clearInterval(queueListInterval);
setQueueListInterval(undefined);
}
}, [connectedToBackend]);
function UpdateMonitoringJobsList(){
JobFunctions.GetJobsInState(apiUri, JobState.Waiting)
.then((jobs: IJob[]) => {
//console.log(jobs);
return jobs;
})
.then(setWaitingJobs);
JobFunctions.GetJobsInState(apiUri, JobState.Running)
.then((jobs: IJob[]) => {
//console.log(jobs);
return jobs;
})
.then(setRunningJobs);
}
return (<>
<div onClick={() => setShowQueuePopup(true)}>
{children}
</div>
{showQueuePopup
? <div className="popup" id="QueuePopUp">
<div className="popupHeader">
<h1>Queue Status</h1>
<img alt="Close Queue" className="close" src="../media/close-x.svg"
onClick={() => setShowQueuePopup(false)}/>
</div>
<div id="QueuePopUpBody" className="popupBody">
<div>
{RunningJobs.filter(j => j.jobType == JobType.DownloadSingleChapterJob).map(j => {
let job = j as IDownloadSingleChapterJob;
return <ChapterItem apiUri={apiUri} chapterId={job.chapterId} />
})}
</div>
<div>
{WaitingJobs.filter(j => j.jobType == JobType.DownloadSingleChapterJob).map(j =>{
let job = j as IDownloadSingleChapterJob;
return <ChapterItem apiUri={apiUri} chapterId={job.chapterId} />
})}
</div>
</div>
</div>
: null
}
</>
);
}

View File

@ -1,150 +0,0 @@
import React, {ChangeEventHandler, EventHandler, useEffect, useState} from 'react';
import {MangaConnectorFunctions} from "./MangaConnectorFunctions";
import IMangaConnector from "./interfaces/IMangaConnector";
import {isValidUri} from "../App";
import IManga, {MangaItem} from "./interfaces/IManga";
import '../styles/search.css';
import SearchFunctions from "./SearchFunctions";
import JobFunctions from "./JobFunctions";
import ILocalLibrary from "./interfaces/ILocalLibrary";
import LocalLibraryFunctions from "./LocalLibraryFunctions";
import Loader from "./Loader";
export default function Search({apiUri, jobInterval, closeSearch} : {apiUri: string, jobInterval: Date, closeSearch(): void}) {
const [mangaConnectors, setConnectors] = useState<IMangaConnector[]>();
const [selectedConnector, setSelectedConnector] = useState<IMangaConnector>();
const [selectedLanguage, setSelectedLanguage] = useState<string>();
const [searchBoxValue, setSearchBoxValue] = useState("");
const [searchResults, setSearchResults] = useState<IManga[]>();
const pattern = /https:\/\/([a-z0-9.]+\.[a-z0-9]{2,})(?:\/.*)?/i
useEffect(() => {
MangaConnectorFunctions.GetAllConnectors(apiUri).then(setConnectors).then(() => setLoading(false));
}, []);
const selectedConnectorChanged : ChangeEventHandler<HTMLSelectElement> = (event) => {
event.preventDefault();
if(mangaConnectors === undefined)
return;
const selectedConnector = mangaConnectors.find((con: IMangaConnector) => con.name == event.target.value);
if(selectedConnector === undefined)
return;
setSelectedConnector(selectedConnector);
setSelectedLanguage(selectedConnector.supportedLanguages[0]);
}
const searchBoxValueChanged : ChangeEventHandler<HTMLInputElement> = (event) => {
event.currentTarget.style.width = event.target.value.length + "ch";
if(mangaConnectors === undefined)
return;
var str : string = event.target.value;
setSearchBoxValue(str);
if(isValidUri(str))
setSelectedConnector(undefined);
const match = str.match(pattern);
if(match === null)
return;
let baseUri = match[1];
const selectCon = mangaConnectors.find((con: IMangaConnector) => {
return con.baseUris.find(uri => uri == baseUri);
});
if(selectCon != undefined){
setSelectedConnector(selectCon);
setSelectedLanguage(selectCon.supportedLanguages[0]);
}
}
const ExecuteSearch : EventHandler<any> = () => {
if(searchBoxValue.length < 1 || selectedConnector === undefined || selectedLanguage === ""){
console.error("Tried initiating search while not all fields where submitted.")
return;
}
//console.info(`Searching Name: ${searchBoxValue} Connector: ${selectedConnector.name} Language: ${selectedLanguage}`);
if(isValidUri(searchBoxValue) && !selectedConnector.baseUris.find((uri: string) => {
const match = searchBoxValue.match(pattern);
if(match === null)
return false;
return match[1] == uri
}))
{
console.error("URL in Searchbox detected, but does not match selected connector.");
return;
}
setLoading(true);
if(!isValidUri(searchBoxValue)){
SearchFunctions.SearchNameOnConnector(apiUri, selectedConnector.name, searchBoxValue)
.then((mangas: IManga[]) => {
setSearchResults(mangas);
})
.finally(()=>setLoading(false));
}else{
SearchFunctions.SearchUrl(apiUri, searchBoxValue)
.then((manga: IManga) => {
setSearchResults([manga]);
})
.finally(()=>setLoading(false));
}
}
const changeSelectedLanguage : ChangeEventHandler<HTMLSelectElement> = (event) => setSelectedLanguage(event.target.value);
let [selectedLibrary, setSelectedLibrary] = useState<ILocalLibrary | null>(null);
let [libraries, setLibraries] = useState<ILocalLibrary[] | null>(null);
let [loading, setLoading] = useState<boolean>(true);
useEffect(() => {
LocalLibraryFunctions.GetLibraries(apiUri).then(setLibraries);
}, []);
useEffect(() => {
if(libraries === null || libraries.length < 1)
setSelectedLibrary(null);
else
setSelectedLibrary(libraries[0]);
}, [libraries]);
const selectedLibraryChanged : ChangeEventHandler<HTMLSelectElement> = (event) => {
event.preventDefault();
if(libraries === null)
return;
const selectedLibrary = libraries.find((lib:ILocalLibrary) => lib.localLibraryId == event.target.value);
if(selectedLibrary === undefined)
return;
setSelectedLibrary(selectedLibrary);
}
return (<div id="Search">
<div id="SearchBox">
<input type="text" placeholder="Manganame" id="Searchbox-Manganame" onKeyDown={(e) => {if(e.key == "Enter") ExecuteSearch(null);}} onChange={searchBoxValueChanged} disabled={loading} />
<select id="Searchbox-Connector" value={selectedConnector === undefined ? "" : selectedConnector.name} onChange={selectedConnectorChanged} disabled={loading}>
{mangaConnectors === undefined ? <option value="Loading">Loading</option> : <option value="" disabled hidden>Select</option>}
{mangaConnectors === undefined
? null
: mangaConnectors.map(con => <option value={con.name} key={con.name}>{con.name}</option>)}
</select>
<select id="Searchbox-language" onChange={changeSelectedLanguage} value={selectedLanguage === null ? "" : selectedLanguage} disabled={loading}>
{mangaConnectors === undefined ? <option value="Loading">Loading</option> : <option value="" disabled hidden>Select Connector</option>}
{selectedConnector === undefined
? null
: selectedConnector.supportedLanguages.map(language => <option value={language} key={language}>{language}</option>)}
</select>
<button id="Searchbox-button" type="submit" onClick={ExecuteSearch} disabled={loading}>Search</button>
<Loader loading={loading} style={{width:"40px", height:"40px"}}/>
</div>
<img alt="Close Search" id="closeSearch" src="../media/close-x.svg" onClick={closeSearch} />
<div id="SearchResults">
{searchResults === undefined
? null
: searchResults.map(result => {
return <MangaItem apiUri={apiUri} mangaId={result.mangaId} >
<select defaultValue={selectedLibrary === null ? "" : selectedLibrary.localLibraryId} onChange={selectedLibraryChanged}>
{selectedLibrary === null || libraries === null ? <option value="">Loading</option>
: libraries.map(library => <option key={library.localLibraryId} value={library.localLibraryId}>{library.libraryName} ({library.basePath})</option>)}
</select>
<button className="Manga-AddButton" onClick={() => {
JobFunctions.CreateDownloadAvailableChaptersJob(apiUri, result.mangaId, {recurrenceTimeMs: new Date(jobInterval).getTime(), localLibraryId: selectedLibrary!.localLibraryId});
}}>Monitor</button>
</MangaItem>
})
}
</div>
</div>);
}

View File

@ -1,46 +0,0 @@
import IManga from "./interfaces/IManga";
import {postData} from "../App";
export default class SearchFunctions {
static async SearchName(apiUri: string, name: string) : Promise<IManga[]> {
if(name === undefined || name === null || name.length < 1) {
console.error(`name was not provided`);
return Promise.reject();
}
return postData(`${apiUri}/v2/Search/Name`, name)
.then((json) => {
const ret = json as IManga[];
return (ret);
});
}
static async SearchNameOnConnector(apiUri: string, connectorName: string, name: string) : Promise<IManga[]> {
if(connectorName === undefined || connectorName === null || connectorName.length < 1) {
console.error(`connectorName was not provided`);
return Promise.reject();
}
if(name === undefined || name === null || name.length < 1) {
console.error(`name was not provided`);
return Promise.reject();
}
return postData(`${apiUri}/v2/Search/${connectorName}`, name)
.then((json) => {
const ret = json as IManga[];
return (ret);
});
}
static async SearchUrl(apiUri: string, url: string) : Promise<IManga> {
if(url === undefined || url === null || url.length < 1) {
console.error(`name was not provided`);
return Promise.reject();
}
return postData(`${apiUri}/v2/Search/Url`, url)
.then((json) => {
const ret = json as IManga;
return (ret);
});
}
}

View File

@ -1,56 +0,0 @@
import IFrontendSettings from "./interfaces/IFrontendSettings";
import '../styles/settings.css';
import '../styles/react-toggle.css';
import React, {useEffect, useState} from "react";
import INotificationConnector, {NotificationConnectorItem} from "./interfaces/INotificationConnector";
import NotificationConnectorFunctions from "./NotificationConnectorFunctions";
export default function Settings({backendConnected, apiUri, frontendSettings, setFrontendSettings} : {backendConnected: boolean, apiUri: string, frontendSettings: IFrontendSettings, setFrontendSettings: (settings: IFrontendSettings) => void}) {
let [showSettings, setShowSettings] = useState<boolean>(false);
let [notificationConnectors, setNotificationConnectors] = useState<INotificationConnector[]>([]);
useEffect(() => {
if(!backendConnected)
return;
NotificationConnectorFunctions.GetNotificationConnectors(apiUri).then(setNotificationConnectors);
}, []);
return (
<div id="Settings">
<div onClick={() => setShowSettings(true)}>
<img id="Settings-Cogwheel" src="../../media/settings-cogwheel.svg" alt="settings-cogwheel" />
</div>
{showSettings
? <div className="popup" id="SettingsPopUp">
<div className="popupHeader">
<h1>Settings</h1>
<img alt="Close Settings" className="close" src="../media/close-x.svg" onClick={() => setShowSettings(false)}/>
</div>
<div id="SettingsPopUpBody" className="popupBody">
<div className="settings-apiuri">
<label>ApiUri</label>
<input type="url" defaultValue={frontendSettings.apiUri} onChange={(e) => {
let newSettings = frontendSettings;
newSettings.apiUri = e.currentTarget.value;
setFrontendSettings(newSettings);
}} id="ApiUri" />
</div>
<div className="settings-apiuri">
<label>Default Job-Interval</label>
<input type="time" defaultValue={new Date(frontendSettings.jobInterval).getTime()} onChange={(e) => {
let newSettings = frontendSettings;
newSettings.jobInterval = e.currentTarget.valueAsDate ?? frontendSettings.jobInterval;
setFrontendSettings(newSettings);
console.log(frontendSettings);
}}/>
</div>
{notificationConnectors.map(c => <NotificationConnectorItem apiUri={apiUri} notificationConnector={c} />)}
<NotificationConnectorItem apiUri={apiUri} notificationConnector={null} />
</div>
</div>
: null
}
</div>
);
}

View File

@ -1,23 +0,0 @@
import React, {ReactElement, useEffect} from "react";
import {getData} from "../../App";
export default interface IAuthor {
authorId: string;
authorName: string;
}
export function AuthorElement({apiUri, authorId} : {apiUri: string, authorId: string | null}) : ReactElement{
let [author, setAuthor] = React.useState<IAuthor | null>(null);
useEffect(()=> {
if(authorId === null)
return;
getData(`${apiUri}/v2/Query/Author/${authorId}`)
.then((json) => {
let ret = json as IAuthor;
setAuthor(ret);
});
}, [])
return (<span className="Manga-Author-Name">{author ? author.authorName : authorId}</span>);
}

View File

@ -1,15 +0,0 @@
export default interface IBackendSettings {
"downloadLocation": string;
"userAgent": string;
"aprilFoolsMode": boolean;
"compression": number;
"bwImages": boolean;
"requestLimits": {
"MangaInfo": number;
"MangaDexFeed": number;
"MangaDexImage": number;
"MangaImage": number;
"MangaCover": number;
"Default": number
}
}

View File

@ -1,51 +0,0 @@
import React, {ReactElement, ReactEventHandler, useEffect, useState} from "react";
import MangaFunctions from "../MangaFunctions";
import IManga from "./IManga";
import ChapterFunctions from "../ChapterFunctions";
export default interface IChapter{
chapterId: string;
volumeNumber: number;
chapterNumber: string;
url: string;
title: string | undefined;
archiveFileName: string;
downloaded: boolean;
parentMangaId: string;
}
export function ChapterItem({apiUri, chapterId} : {apiUri: string, chapterId: string}) : ReactElement {
const setCoverItem : ReactEventHandler<HTMLImageElement> = (e) => {
setMangaCoverHtmlImageItem(e.currentTarget);
}
let [chapter, setChapter] = useState<IChapter | null>(null);
let [manga, setManga] = useState<IManga | null>(null);
let [mangaCoverUrl, setMangaCoverUrl] = useState<string>("../../media/blahaj.png");
let [mangaCoverHtmlImageItem, setMangaCoverHtmlImageItem] = useState<HTMLImageElement | null>(null);
useEffect(() => {
ChapterFunctions.GetChapterFromId(apiUri, chapterId).then(setChapter);
}, []);
useEffect(() => {
if(chapter === null)
manga = null;
else
MangaFunctions.GetMangaById(apiUri, chapter.parentMangaId).then(setManga);
}, [chapter]);
useEffect(() => {
if(chapter != null && mangaCoverHtmlImageItem != null)
setMangaCoverUrl(MangaFunctions.GetMangaCoverImageUrl(apiUri, chapter.parentMangaId, mangaCoverHtmlImageItem));
}, [chapter, mangaCoverHtmlImageItem]);
let [clicked, setClicked] = useState<boolean>(false);
return (<div className="ChapterItem" key={chapterId} is-clicked={clicked ? "clicked" : "not-clicked"} onClick={() => setClicked(!clicked)}>
<img className="ChapterItem-Cover" src={mangaCoverUrl} alt="MangaFunctions Cover" onLoad={setCoverItem} onResize={setCoverItem}></img>
<p className="ChapterItem-MangaName">{manga ? manga.name : "MangaFunctions-Name"}</p>
<p className="ChapterItem-ChapterName">{chapter ? chapter.title : "ChapterFunctions-Title"}</p>
<p className="ChapterItem-Volume">Vol.{chapter ? chapter.volumeNumber : "VolNum"}</p>
<p className="ChapterItem-Chapter">Ch.{chapter ? chapter.chapterNumber : "ChNum"}</p>
<p className="ChapterItem-VolumeChapter">Vol.{chapter ? chapter.volumeNumber : "VolNum"} Ch.{chapter ? chapter.chapterNumber : "ChNum"}</p>
<a className="ChapterItem-Website" href={chapter ? chapter.url : "#"}><img src="../../media/link.svg" alt="Link"/></a>
</div>)
}

View File

@ -1,18 +0,0 @@
import {Cookies} from "react-cookie";
export default interface IFrontendSettings {
jobInterval: Date;
apiUri: string;
}
export function LoadFrontendSettings(): IFrontendSettings {
const cookies = new Cookies();
return {
jobInterval: cookies.get('jobInterval') === undefined
? new Date(Date.parse("1970-01-01T03:00:00.000Z"))
: cookies.get('jobInterval'),
apiUri: cookies.get('apiUri') === undefined
? `${window.location.protocol}//${window.location.host}/api`
: cookies.get('apiUri')
}
}

View File

@ -1,24 +0,0 @@
import React, {ReactElement, useEffect} from "react";
import {getData} from "../../App";
export default interface ILink {
linkId: string;
linkProvider: string;
linkUrl: string;
}
export function LinkElement({apiUri, linkId} : {apiUri: string, linkId: string | null}) : ReactElement{
let [link, setLink] = React.useState<ILink | null>(null);
useEffect(()=> {
if(linkId === null)
return;
getData(`${apiUri}/v2/Query/Link/${linkId}`)
.then((json) => {
let ret = json as ILink;
setLink(ret);
});
}, [])
return (<a className="Manga-Link-Value" href={link ? link.linkUrl : "#"}>{link ? link.linkProvider : linkId}</a>);
}

View File

@ -1,14 +0,0 @@
import {ReactElement} from "react";
export default interface ILocalLibrary {
localLibraryId: string;
basePath: string;
libraryName: string;
}
export function LocalLibrary(library: ILocalLibrary) : ReactElement {
return (<div key={library.localLibraryId}>
<p className={"LocalLibraryFunctions-Name"}>{library.libraryName}</p>
<p className={"LocalLibraryFunctions-Path"}>{library.basePath}</p>
</div>);
}

View File

@ -1,129 +0,0 @@
import MangaFunctions from "../MangaFunctions";
import React, {Children, ReactElement, ReactEventHandler, useEffect, useState} from "react";
import Icon from '@mdi/react';
import { mdiTagTextOutline, mdiAccountEdit, mdiLinkVariant } from '@mdi/js';
import MarkdownPreview from '@uiw/react-markdown-preview';
import {AuthorElement} from "./IAuthor";
import {LinkElement} from "./ILink";
import IChapter from "./IChapter";
import Loader from "../Loader";
export default interface IManga{
mangaId: string;
idOnConnectorSite: string;
name: string;
description: string;
websiteUrl: string;
year: number;
originalLanguage: string;
releaseStatus: MangaReleaseStatus;
folderName: string;
ignoreChapterBefore: number;
mangaConnectorId: string;
authorIds: string[];
tags: string[];
linkIds: string[];
altTitleIds: string[];
}
export enum MangaReleaseStatus {
Continuing = "Continuing",
Completed = "Completed",
OnHiatus = "OnHiatus",
Cancelled = "Cancelled",
Unreleased = "Unreleased",
}
export function MangaItem({apiUri, mangaId, children} : {apiUri: string, mangaId: string, children?: (string | ReactElement)[]}) : ReactElement {
const LoadMangaCover : ReactEventHandler<HTMLImageElement> = (e) => {
if(e.currentTarget.src != MangaFunctions.GetMangaCoverImageUrl(apiUri, mangaId, e.currentTarget))
e.currentTarget.src = MangaFunctions.GetMangaCoverImageUrl(apiUri, mangaId, e.currentTarget);
}
let [manga, setManga] = useState<IManga | null>(null);
let [clicked, setClicked] = useState<boolean>(false);
let [latestChapterDownloaded, setLatestChapterDownloaded] = useState<IChapter | null>(null);
let [latestChapterAvailable, setLatestChapterAvailable] = useState<IChapter | null>(null);
let [loadingChapterStats, setLoadingChapterStats] = useState<boolean>(true);
let [settingThreshold, setSettingThreshold] = useState<boolean>(false);
const invalidTargets = ["input", "textarea", "button", "select", "a"];
useEffect(() => {
MangaFunctions.GetMangaById(apiUri, mangaId).then(setManga);
MangaFunctions.GetLatestChapterDownloaded(apiUri, mangaId)
.then(setLatestChapterDownloaded)
.finally(() => {
if(latestChapterDownloaded && latestChapterAvailable)
setLoadingChapterStats(false);
});
MangaFunctions.GetLatestChapterAvailable(apiUri, mangaId)
.then(setLatestChapterAvailable)
.finally(() => {
if(latestChapterDownloaded && latestChapterAvailable)
setLoadingChapterStats(false);
});
}, []);
return (<div className="MangaItem" key={mangaId} is-clicked={clicked ? "clicked" : "not-clicked"} onClick={(e)=> {
e.preventDefault();
const target = e.target as HTMLElement;
if(invalidTargets.find(x => x == target.localName) === undefined )
setClicked(!clicked)
}}>
<img className="MangaItem-Cover" src="../../media/blahaj.png" alt="MangaFunctions Cover" onLoad={LoadMangaCover} onResize={LoadMangaCover}></img>
<div className="MangaItem-Connector">{manga ? manga.mangaConnectorId : "Connector"}</div>
<div className="MangaItem-Status" release-status={manga?.releaseStatus}></div>
<div className="MangaItem-Name">{manga ? manga.name : "Name"}</div>
<a className="MangaItem-Website" href={manga ? manga.websiteUrl : "#"}><img src="../../media/link.svg" alt="Link"/></a>
<div className="MangaItem-Tags">
{manga ? manga.authorIds.map(authorId =>
<div className="MangaItem-Author" key={authorId} >
<Icon path={mdiAccountEdit} size={0.5} />
<AuthorElement apiUri={apiUri} authorId={authorId}></AuthorElement>
</div>)
:
<div className="MangaItem-Author" key="null-Author">
<Icon path={mdiAccountEdit} size={0.5} />
<AuthorElement apiUri={apiUri} authorId={null}></AuthorElement>
</div>}
{manga ? manga.tags.map(tag =>
<div className="MangaItem-Tag" key={tag}>
<Icon path={mdiTagTextOutline} size={0.5}/>
<span className="MangaItem-Tag-Value">{tag}</span>
</div>)
:
<div className="MangaItem-Tag" key="null-Tag">
<Icon path={mdiTagTextOutline} size={0.5}/>
<span className="MangaItem-Tag-Value">Tag</span>
</div>
}
{manga ? manga.linkIds.map(linkId =>
<div className="MangaItem-Link" key={linkId}>
<Icon path={mdiLinkVariant} size={0.5}/>
<LinkElement apiUri={apiUri} linkId={linkId} />
</div>)
:
<div className="MangaItem-Link" key="null-Link">
<Icon path={mdiLinkVariant} size={0.5}/>
<LinkElement apiUri={apiUri} linkId={null} />
</div>}
</div>
<MarkdownPreview className="MangaItem-Description" source={manga ? manga.description : "# Description"} />
<div className="MangaItem-Props">
<div className="MangaItem-Props-Threshold">
Start at Chapter
<input type="text" defaultValue={latestChapterDownloaded ? latestChapterDownloaded.chapterNumber : ""} disabled={settingThreshold} onChange={(e) => {
setSettingThreshold(true);
MangaFunctions.SetIgnoreThreshold(apiUri, mangaId, e.currentTarget.valueAsNumber).finally(()=>setSettingThreshold(false));
}} />
<Loader loading={settingThreshold} />
out of <span className="MangaItem-Props-Threshold-Available">{latestChapterAvailable ? latestChapterAvailable.chapterNumber : <Loader loading={loadingChapterStats}/>}</span>
</div>
{children ? children.map(c => {
if(c instanceof Element)
return c as ReactElement;
else
return c
}) : null}
</div>
</div>)
}

View File

@ -1,25 +0,0 @@
import React, {ReactElement, useEffect} from "react";
import {getData} from "../../App";
import IAuthor from "./IAuthor";
export default interface IMangaAltTitle {
altTitleId: string;
language: string;
title: string;
}
export function AltTitleElement({apiUri, altTitleId} : {apiUri: string, altTitleId: string | null}) : ReactElement{
let [altTitle, setAltTitle] = React.useState<IMangaAltTitle | null>(null);
useEffect(()=> {
if(altTitleId === null)
return;
getData(`${apiUri}/v2/Query/AltTitle/${altTitleId}`)
.then((json) => {
let ret = json as IMangaAltTitle;
setAltTitle(ret);
});
}, [])
return (<span className="Manga-AltTitle">{altTitle ? altTitle.title : altTitleId}</span>);
}

View File

@ -1,88 +0,0 @@
import {ReactElement, ReactEventHandler, useState} from "react";
import "../../styles/notificationConnector.css";
import Loader from "../Loader";
import NotificationConnectorFunctions from "../NotificationConnectorFunctions";
export default interface INotificationConnector {
name: string;
url: string;
headers: Record<string, string>[];
httpMethod: string;
body: string;
}
export function NotificationConnectorItem({apiUri, notificationConnector} : {apiUri: string, notificationConnector: INotificationConnector | null}) : ReactElement {
const AddHeader : ReactEventHandler<HTMLButtonElement> = (e) => {
let header : Record<string, string> = {};
let x = info;
x.headers = [header, ...x.headers];
setInfo(x);
setHeaderElements([...headerElements, <HeaderElement record={header} />])
}
const [headerElements, setHeaderElements] = useState<ReactElement[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [info, setInfo] = useState<INotificationConnector>({
name: "",
url: "",
headers: [],
httpMethod: "",
body: ""
});
return (<div className="NotificationConnectorItem" key={notificationConnector ? notificationConnector.name : "new"}>
<p className="NotificationConnectorItem-Name">{notificationConnector ? notificationConnector.name : "New Notification Connector"}</p>
<div className="NotificationConnectorItem-Url">
<select className="NotificationConnectorItem-RequestMethod" defaultValue={notificationConnector ? notificationConnector.httpMethod : ""} disabled={notificationConnector != null} onChange={(e) => {
let x = info;
x.httpMethod = e.currentTarget.value;
setInfo(x);
}}>
<option value="" disabled hidden>Request Method</option>
<option value="GET">GET</option>
<option value="POST">POST</option>
</select>
<input type="url" className="NotificationConnectorItem-RequestUrl" placeholder="URL" defaultValue={notificationConnector ? notificationConnector.url : ""} disabled={notificationConnector != null} onChange={(e) => {
let x = info;
x.url = e.currentTarget.value;
setInfo(x);
}} />
</div>
<textarea className="NotificationConnectorItem-Body" placeholder="Request-Body" defaultValue={notificationConnector ? notificationConnector.body : ""} disabled={notificationConnector != null} onChange={(e) => {
let x = info;
x.body = e.currentTarget.value;
setInfo(x);
}} />
{notificationConnector != null ? null :
(
<p className="NotificationConnectorItem-Explanation">Explanation Text</p>
)}
<div className="NotificationConnectorItem-Headers">
{headerElements}
{notificationConnector
? notificationConnector.headers.map((h: Record<string, string>) =>
(<HeaderElement record={h} disabled={notificationConnector != null}/>)
) :
(
<button className="NotificationConnectorItem-AddHeader" onClick={AddHeader}>Add Header</button>
)
}
</div>
{notificationConnector != null ? null : (
<>
<button className="NotificationConnectorItem-Save" onClick={(e) => {
setLoading(true);
NotificationConnectorFunctions.CreateNotificationConnector(apiUri, info)
.finally(() => setLoading(false));
}}>Add</button>
<Loader loading={loading} style={{width:"40px",height:"40px",margin:"calc(sin(70)*(50% - 40px))"}}/>
</>
)}
</div>);
}
function HeaderElement({record, disabled} : {record: Record<string, string>, disabled?: boolean | null}) : ReactElement {
return <div className="NotificationConnectorItem-Header" key={record.name}>
<input type="text" className="NotificationConnectorItem-Header-Key" placeholder="Header-Key" defaultValue={record.name} disabled={disabled?disabled:false} onChange={(e) => record.name = e.currentTarget.value}/>
<input type="text" className="NotificationConnectorItem-Header-Value" placeholder="Header-Value" defaultValue={record.value} disabled={disabled?disabled:false} onChange={(e) => record.value = e.currentTarget.value} />
</div>;
}

View File

@ -1,5 +0,0 @@
import IJob from "./IJob";
export default interface IDownloadAvailableChaptersJob extends IJob {
mangaId: string;
}

View File

@ -1,5 +0,0 @@
import IJob from "./IJob";
export default interface IDownloadMangaCoverJob extends IJob {
mangaId: string;
}

View File

@ -1,5 +0,0 @@
import IJob from "./IJob";
export default interface IDownloadSingleChapterJob extends IJob {
chapterId: string;
}

View File

@ -1,6 +0,0 @@
import IJob from "./IJob";
export default interface IMoveMangaLibraryJob extends IJob {
MangaId: string;
ToLibraryId: string;
}

View File

@ -1,5 +0,0 @@
import IJob from "./IJob";
export default interface IRetrieveChaptersJob extends IJob {
mangaId: string;
}

View File

@ -1,5 +0,0 @@
import IJob from "./IJob";
export default interface IUpdateFilesDownloadedJob extends IJob {
mangaId: string;
}

View File

@ -1,5 +0,0 @@
import IJob from "./IJob";
export default interface IUpdateMetadataJob extends IJob {
mangaId: string;
}

View File

@ -1,4 +0,0 @@
export default interface IDownloadAvailableJobsRecord {
recurrenceTimeMs: number;
localLibraryId: string;
}

View File

@ -1,3 +0,0 @@
export default interface ILunaseaRecord {
id: string;
}

View File

@ -1,4 +0,0 @@
export default interface INewLibraryRecord {
path: string;
name: string;
}

View File

@ -1,42 +0,0 @@
footer {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
width: 100%;
height: 40px;
align-items: center;
justify-content: center;
background-color: var(--primary-color);
align-content: center;
position: fixed;
bottom: 0;
color: white;
z-index: 10;
}
#madeWith {
flex-grow: 1;
text-align: right;
margin-right: 20px;
cursor: url("Website/media/blahaj.png"), grab;
}
footer .statusBadge {
margin: 0 10px;
display: flex;
align-items: center;
justify-items: center;
background-color: rgba(255,255,255, 0.3);
border-radius: 10px;
padding: 2px 5px;
}
footer > div {
display: flex;
align-items: center;
justify-items: center;
}
footer .hoverHand {
cursor: pointer;
}

View File

@ -1,37 +0,0 @@
header {
display: flex;
align-items: center;
justify-content: space-between;
height: var(--topbar-height);
background-color: var(--secondary-color);
z-index: 100;
box-shadow: 0 0 20px black;
}
header > #titlebox {
position: relative;
display: flex;
margin: 0 0 0 40px;
height: 100%;
align-items:center;
justify-content:center;
}
header > #titlebox > span{
cursor: default;
font-size: 24pt;
font-weight: bold;
background: linear-gradient(150deg, var(--primary-color), var(--accent-color));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-left: 20px;
}
header > #titlebox > img {
height: 100%;
cursor: grab;
}
header > * {
height: 100%;
}

View File

@ -1,45 +0,0 @@
:root{
--background-color: #030304;
--second-background-color: white;
--primary-color: #f5a9b8;
--secondary-color: #5bcefa;
--blur-background: rgba(245, 169, 184, 0.58);
--accent-color: #fff;
/* --primary-color: green;
--secondary-color: gold;
--blur-background: rgba(86, 131, 36, 0.8);
--accent-color: olive; */
--topbar-height: 60px;
box-sizing: border-box;
scrollbar-color: var(--primary-color) var(--second-background-color);
scroll-behavior: smooth;
scrollbar-width: thin;
}
body{
padding: 0;
margin: 0;
height: 100vh;
background-color: var(--background-color);
font-family: "Inter", sans-serif;
overflow-x: hidden;
color: var(--primary-color);
}
.tooltip {
position: relative;
}
.tooltip:hover:before {
display: block;
content: attr(data-tooltip, "tooltip");
background-color: var(--second-background-color);
color: var(--secondary-color);
border: 1px solid var(--secondary-color);
border-radius: 6px;
bottom: 1em;
max-width: 90%;
position: absolute;
padding: 3px 7px 1px;
z-index: 999;
}

View File

@ -1,80 +0,0 @@
span[is-loading="done"] {
display: none;
}
span[is-loading="loading"] {
transform: rotateZ(45deg);
perspective: 1000px;
border-radius: 50%;
width: 32px;
height: 32px;
color: var(--second-background-color);
position: fixed;
background-color: var(--secondary-color);
background-blend-mode: lighten;
}
span[is-loading="loading"]:before,
span[is-loading="loading"]:after {
content: '';
display: block;
position: absolute;
top: 0;
left: 0;
width: inherit;
height: inherit;
border-radius: 50%;
transform: rotateX(70deg);
animation: 1s spin linear infinite;
}
span[is-loading="loading"]:after {
color: var(--primary-color);
transform: rotateY(70deg);
animation-delay: .4s;
}
@keyframes rotate {
0% {
transform: translate(-50%, -50%) rotateZ(0deg);
}
100% {
transform: translate(-50%, -50%) rotateZ(360deg);
}
}
@keyframes rotateccw {
0% {
transform: translate(-50%, -50%) rotate(0deg);
}
100% {
transform: translate(-50%, -50%) rotate(-360deg);
}
}
@keyframes spin {
0%,
100% {
box-shadow: .2em 0px 0 0px currentcolor;
}
12% {
box-shadow: .2em .2em 0 0 currentcolor;
}
25% {
box-shadow: 0 .2em 0 0px currentcolor;
}
37% {
box-shadow: -.2em .2em 0 0 currentcolor;
}
50% {
box-shadow: -.2em 0 0 0 currentcolor;
}
62% {
box-shadow: -.2em -.2em 0 0 currentcolor;
}
75% {
box-shadow: 0px -.2em 0 0 currentcolor;
}
87% {
box-shadow: .2em -.2em 0 0 currentcolor;
}
}

View File

@ -1,253 +0,0 @@
.MangaItem{
cursor: pointer;
background-color: var(--secondary-color);
width: 200px;
height: 300px;
border-radius: 5px;
overflow: hidden;
position: relative;
flex-shrink: 0;
display: grid;
grid-template-columns: 200px 600px;
grid-template-rows: 80px 190px 30px;
grid-template-areas:
"cover tags"
"cover description"
"cover footer";
margin: 5px;
}
.MangaItem[is-clicked="clicked"]{
width: 800px;
}
.MangaItem::after{
content: '';
position: absolute;
left: 0; top: 0;
border-radius: 5px;
width: 100%; height: 100%;
background: linear-gradient(rgba(0,0,0,0.8), rgba(0, 0, 0, 0.7),rgba(0, 0, 0, 0.2));
z-index: 0;
}
.MangaItem > * {
z-index: 2;
position: relative;
}
.startSearchEntry::after{
background: initial !important;
}
.MangaItem-Connector {
grid-area: cover;
top: 10px;
left: 10px;
flex-grow: 0;
height: 14pt;
font-size: 12pt;
border-radius: 9pt;
background-color: var(--primary-color);
padding: 2pt 17px;
color: black;
width: fit-content;
z-index: 1;
}
.MangaItem-Name{
grid-area: cover;
width: fit-content;
font-size: 16pt;
font-weight: bold;
color: white;
top: 50px;
left: 10px;
margin: 0;
max-width: calc(100% - 20px);
z-index: 1;
}
.MangaItem-Website {
grid-area: cover;
display: block;
height: 13px;
width: 13px;
margin: 0px 0px auto auto;
top: 12px;
right: 10px;
z-index: 1;
}
.MangaItem-Status {
grid-area: cover;
display:block;
height: 15px;
width: 15px;
border-radius: 50%;
position: absolute;
top: 14px;
right: 35px;
box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 10px, rgb(51, 51, 51) 0px 0px 10px 3px;
z-index: 1;
}
.MangaItem-Status::after {
content: attr(release-status);
position: absolute;
top: -2.5pt;
right: 0;
visibility: hidden;
/*Text Properties*/
font-size:10pt;
font-weight:bold;
color:white;
text-align: center;
/*Size*/
padding: 3px 8px;
border-radius: 6px;
border: 0px;
background-color: inherit;
}
.MangaItem-Status:hover::after{
visibility:visible;
}
.MangaItem-Status[release-status="Continuing"]{
background-color: limegreen;
}
.MangaItem-Status[release-status="Completed"]{
background-color: blueviolet;
}
.MangaItem-Status[release-status="On Hiatus"]{
background-color: darkorange;
}
.MangaItem-Status[release-status="Cancelled"]{
background-color: firebrick;
}
.MangaItem-Status[release-status="Unreleased"]{
background-color: aqua;
}
.MangaItem-Status[release-status="Status Unavailable"]{
background-color: gray;
}
.MangaItem-Cover {
position: absolute;
top: 0;
left: 0;
grid-area: cover;
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 5px;
z-index: 0;
}
.MangaItem[is-clicked="not-clicked"] .MangaItem-Description, .MangaItem[is-clicked="not-clicked"] .MangaItem-Tags, .MangaItem[is-clicked="not-clicked"] .MangaItem-Props{
display: none !important;
}
.MangaItem[is-clicked="clicked"] .MangaItem-Description, .MangaItem[is-clicked="clicked"] .MangaItem-Tags, .MangaItem[is-clicked="clicked"] .MangaItem-Props{
display: block;
width: calc(100% - 6px);
background-color: white;
padding: 0 3px;
overflow-y: auto;
}
.MangaItem-Tags {
grid-area: tags;
display: flex !important;
flex-direction: row;
flex-wrap: wrap;
scrollbar-width: thin;
border-bottom: 1px solid var(--secondary-color);
justify-content: start;
align-content: start;
}
.MangaItem-Tags > * {
display: flex;
flex-direction: row;
width: max-content;
margin: 1px 2px !important;
white-space: nowrap;
color: white;
padding: 3px 2px 4px 2px;
position: relative;
}
.MangaItem-Tags > * > * {
margin: 0 2px !important;
align-items: center;
}
.MangaItem-Tags > * > svg {
margin: auto 2px !important;
}
.MangaItem-Tags a, .MangaItem-Tags a:visited {
color: var(--primary-color);
text-decoration: none;
}
.MangaItem-Author {
background-color: green;
}
.MangaItem-Tag {
background-color: blue;
}
.MangaItem-Link{
background-color: brown;
}
.MangaItem-Description {
grid-area: description;
color: black;
max-height: 40vh;
scrollbar-width: thin;
border-bottom: 1px solid var(--secondary-color);
}
.MangaItem-Props {
grid-area: footer;
display: flex !important;
flex-direction: row;
justify-content: flex-end;
width: 100%;
height: 100%;
}
.MangaItem-Props > * {
margin: 3px;
border: 1px solid var(--primary-color);
border-radius: 3px;
background-color: transparent;
width: fit-content;
}
.MangaItem-Props-Threshold {
color: black;
padding: 0 3px;
}
.MangaItem-Props-Threshold > input {
margin: 0 3px;
width: 50px;
}
.MangaItem-Props-Threshold-Available{
text-decoration: underline;
}

View File

@ -1,37 +0,0 @@
#MonitorMangaList {
position: relative;
display: flex;
flex-flow: row;
flex-wrap: nowrap;
flex-grow: 1;
overflow-y: auto;
margin: 5px 0;
}
.MangaActionButtons {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.MangaActionButtons > div {
position: absolute;
margin: 10px;
border: 0;
background: none;
cursor: pointer;
}
.DeleteJobButton {
bottom: 0;
left: 0;
filter: invert(21%) sepia(63%) saturate(7443%) hue-rotate(355deg) brightness(93%) contrast(118%);
}
.StartJobNowButton {
bottom: 0;
right: 0;
filter: invert(58%) sepia(16%) saturate(4393%) hue-rotate(103deg) brightness(102%) contrast(103%);
}

View File

@ -1,69 +0,0 @@
.NotificationConnectorItem{
position: relative;
display: grid;
width: calc(100% - 10px);
grid-template-columns: 40% calc(60% - 10px);
margin: 0 auto;
padding: 5px;
border-radius: 5px;
grid-template-rows: 30px 30px auto 30px;
column-gap: 4px;
row-gap: 4px;
grid-template-areas:
"name name"
"url explanation "
"headers body"
"footer footer";
align-items: center;
border: 1px solid var(--primary-color);
}
.NotificationConnectorItem p{
margin: 0;
}
.NotificationConnectorItem-Name{
grid-area: name;
justify-self: flex-start;
}
.NotificationConnectorItem-Url{
grid-area: url;
}
.NotificationConnectorItem-Body{
grid-area: body;
width: calc(100% - 2px);
height: max-content;
min-height: 100%;
padding: 0;
margin: 0;
resize: vertical;
}
.NotificationConnectorItem-Explanation{
grid-area: explanation;
align-self: flex-end;
}
.NotificationConnectorItem-Headers{
grid-area: headers;
justify-self: flex-end;
align-self: flex-end;
display: flex;
flex-direction: column;
}
.NotificationConnectorItem-Header {
display: flex;
flex-direction: row;
}
.NotificationConnectorItem-Header > input {
width: 48%;
}
.NotificationConnectorItem-Save{
grid-area: footer;
justify-self: flex-end;
padding: 0 15px;
}

View File

@ -1,44 +0,0 @@
.popup {
position: fixed;
left: 10%;
top: 7.5%;
width: 80%;
height: 80%;
margin: auto;
z-index: 100;
background-color: var(--second-background-color);
border-radius: 10px;
overflow: hidden;
}
.popup .popupHeader {
position: absolute;
top: 0;
left: 0;
height: 40px;
width: 100%;
background-color: var(--primary-color);
color: var(--accent-color);
}
.popup .popupHeader h1 {
margin: 4px 10px;
font-size: 20pt;
}
.popup .close {
position: absolute;
top: 0;
right: 0;
height: 100%;
cursor: pointer;
}
.popup .popupBody {
position: absolute;
top: 40px;
left: 0;
width: calc(100% - 30px);
height: calc(100% - 50px);
margin: 5px 15px;
}

View File

@ -1,82 +0,0 @@
#QueuePopUp #QueuePopUpBody {
display: flex;
color: black;
}
#QueuePopUp #QueuePopUpBody > * {
padding: 20px;
width: calc(50% - 40px);
height: calc(100% - 40px);
overflow-y: scroll;
}
#QueuePopUp #QueuePopUpBody h1 {
padding: 0;
margin: 0 0 5px 0;
color: var(--primary-color);
}
#QueuePopUp #QueuePopUpBody > *:first-child {
border-right: 1px solid var(--primary-color);
}
#QueuePopUp #QueuePopUpBody .JobQueue {
display: flex;
flex-direction: column;
}
.QueueJob {
color: black;
margin: 5px 0;
position: relative;
height: 200px;
display: grid;
grid-template-columns: 150px auto;
grid-template-rows: 25% 20% auto 15% 12%;
column-gap: 10px;
grid-template-areas:
"cover name"
"cover jobType"
"cover additionalInfo"
"cover progress"
"cover actions"
}
.QueueJob p {
margin: 2px 0;
}
.QueueJob img{
grid-area: cover;
height: 100%;
max-width: 100%;
}
.QueueJob .QueueJob-Name{
grid-area: name;
font-weight: bold;
font-size: 14pt;
}
.QueueJob .JobType{
grid-area: jobType;
}
.QueueJob .QueueJob-additionalInfo {
grid-area: additionalInfo;
}
.QueueJob .QueueJob-actions {
grid-area: actions;
display: flex;
justify-content: space-evenly;
}
.QueueJob .QueueJob-Cancel {
grid-area: actions;
width: 150px;
}
.QueueJob .QueueJob-Progressbar {
grid-area: progress;
}

View File

@ -1,143 +0,0 @@
/* https://raw.githubusercontent.com/instructure-react/react-toggle/master/style.css */
.react-toggle {
touch-action: pan-x;
display: inline-block;
position: relative;
cursor: pointer;
background-color: transparent;
border: 0;
padding: 0;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-tap-highlight-color: transparent;
}
.react-toggle-screenreader-only {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
.react-toggle--disabled {
cursor: not-allowed;
opacity: 0.5;
-webkit-transition: opacity 0.25s;
transition: opacity 0.25s;
}
.react-toggle-track {
width: 50px;
height: 24px;
padding: 0;
border-radius: 30px;
background-color: #4D4D4D;
-webkit-transition: all 0.2s ease;
-moz-transition: all 0.2s ease;
transition: all 0.2s ease;
}
.react-toggle:hover:not(.react-toggle--disabled) .react-toggle-track {
background-color: #000000;
}
.react-toggle--checked .react-toggle-track {
background-color: #19AB27;
}
.react-toggle--checked:hover:not(.react-toggle--disabled) .react-toggle-track {
background-color: #128D15;
}
.react-toggle-track-check {
position: absolute;
width: 14px;
height: 10px;
top: 0px;
bottom: 0px;
margin-top: auto;
margin-bottom: auto;
line-height: 0;
left: 8px;
opacity: 0;
-webkit-transition: opacity 0.25s ease;
-moz-transition: opacity 0.25s ease;
transition: opacity 0.25s ease;
}
.react-toggle--checked .react-toggle-track-check {
opacity: 1;
-webkit-transition: opacity 0.25s ease;
-moz-transition: opacity 0.25s ease;
transition: opacity 0.25s ease;
}
.react-toggle-track-x {
position: absolute;
width: 10px;
height: 10px;
top: 0px;
bottom: 0px;
margin-top: auto;
margin-bottom: auto;
line-height: 0;
right: 10px;
opacity: 1;
-webkit-transition: opacity 0.25s ease;
-moz-transition: opacity 0.25s ease;
transition: opacity 0.25s ease;
}
.react-toggle--checked .react-toggle-track-x {
opacity: 0;
}
.react-toggle-thumb {
transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1) 0ms;
position: absolute;
top: 1px;
left: 1px;
width: 22px;
height: 22px;
border: 1px solid #4D4D4D;
border-radius: 50%;
background-color: #FAFAFA;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-transition: all 0.25s ease;
-moz-transition: all 0.25s ease;
transition: all 0.25s ease;
}
.react-toggle--checked .react-toggle-thumb {
left: 27px;
border-color: #19AB27;
}
.react-toggle--focus .react-toggle-thumb {
-webkit-box-shadow: 0px 0px 3px 2px #0099E0;
-moz-box-shadow: 0px 0px 3px 2px #0099E0;
box-shadow: 0px 0px 2px 3px #0099E0;
}
.react-toggle:active:not(.react-toggle--disabled) .react-toggle-thumb {
-webkit-box-shadow: 0px 0px 5px 5px #0099E0;
-moz-box-shadow: 0px 0px 5px 5px #0099E0;
box-shadow: 0px 0px 5px 5px #0099E0;
}

View File

@ -1,61 +0,0 @@
#Search{
position: relative;
width: 100vw;
margin: auto;
}
#SearchBox{
display: flex;
align-content: center;
justify-content: center;
margin: 10px 0;
}
#SearchResults {
width: 100%;
display: flex;
flex-flow: row wrap;
justify-content: center;
}
#SearchBox select, #SearchBox button, #SearchBox input {
border-color: var(--primary-color);
border-style: solid;
border-width: 0;
border-bottom-width: 2px;
border-top-width: 2px;
padding: 2px 5px;
font-size: 12pt;
}
#Searchbox-Manganame {
border-bottom-left-radius: 2px;
border-top-left-radius: 2px;
border-left-width: 2px !important;
min-width: 300px;
max-width: 50vw;
}
#Searchbox-connector {
width: max-content;
}
#Searchbox-language {
width: 90px;
}
#Searchbox-button {
border-bottom-right-radius: 2px;
border-top-right-radius: 2px;
border-right-width: 2px !important;
width: 90px;
}
#closeSearch {
position: absolute;
top: 0;
right: 10px;
width: 30px;
height: 30px;
filter: brightness(0) saturate(100%) invert(100%) sepia(100%) saturate(1%) hue-rotate(20deg) brightness(103%) contrast(101%);
}

View File

@ -1,25 +0,0 @@
#Settings {
height: 100%;
display: flex;
align-items: center;
}
#Settings > * {
height: 80%;
}
#Settings > div > img {
height: calc(100% - 10px);
margin: 5px;
filter: invert(100%) sepia(20%) saturate(7480%) hue-rotate(179deg) brightness(121%) contrast(102%);
}
#SettingsPopUpBody {
display: flex;
flex-direction: column;
justify-content: flex-start;
}
#SettingsPopUpBody > * {
margin: 5px 0;
}

26
nginx/fastcgi.conf Normal file
View File

@ -0,0 +1,26 @@
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;

Some files were not shown because too many files have changed in this diff Show More