mirror of
https://github.com/tachiyomiorg/website.git
synced 2024-12-21 07:31:58 +01:00
Add initial support to Download page (#7)
Also optimize parts of Extensions page
This commit is contained in:
parent
7412eeacd6
commit
ea29dac3f8
@ -40,6 +40,8 @@
|
||||
"@mdit/plugin-img-size": "^0.4.8",
|
||||
"@mdit/plugin-include": "^0.4.8",
|
||||
"@mdit/plugin-tab": "^0.4.8",
|
||||
"@types/lodash.groupby": "^4.6.7",
|
||||
"@types/markdown-it": "^13.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^6.5.0",
|
||||
"@typescript-eslint/parser": "^6.5.0",
|
||||
"eslint": "^8.48.0",
|
||||
@ -70,9 +72,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@mdi/js": "^7.2.96",
|
||||
"@tanstack/vue-query": "^4.34.0",
|
||||
"axios": "^1.5.0",
|
||||
"element-plus": "^2.3.12",
|
||||
"lodash.groupby": "^4.6.0",
|
||||
"markdown-it": "^13.0.1",
|
||||
"moment": "^2.29.4",
|
||||
"vitepress-plugin-auto-sidebar": "^1.1.0"
|
||||
}
|
||||
}
|
||||
|
94
website/pnpm-lock.yaml
generated
94
website/pnpm-lock.yaml
generated
@ -1,13 +1,12 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@mdi/js':
|
||||
specifier: ^7.2.96
|
||||
version: 7.2.96
|
||||
'@tanstack/vue-query':
|
||||
specifier: ^4.34.0
|
||||
version: 4.34.0(vue@3.3.4)
|
||||
axios:
|
||||
specifier: ^1.5.0
|
||||
version: 1.5.0
|
||||
@ -17,6 +16,12 @@ dependencies:
|
||||
lodash.groupby:
|
||||
specifier: ^4.6.0
|
||||
version: 4.6.0
|
||||
markdown-it:
|
||||
specifier: ^13.0.1
|
||||
version: 13.0.1
|
||||
moment:
|
||||
specifier: ^2.29.4
|
||||
version: 2.29.4
|
||||
vitepress-plugin-auto-sidebar:
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0
|
||||
@ -43,6 +48,12 @@ devDependencies:
|
||||
'@mdit/plugin-tab':
|
||||
specifier: ^0.4.8
|
||||
version: 0.4.8
|
||||
'@types/lodash.groupby':
|
||||
specifier: ^4.6.7
|
||||
version: 4.6.7
|
||||
'@types/markdown-it':
|
||||
specifier: ^13.0.1
|
||||
version: 13.0.1
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ^6.5.0
|
||||
version: 6.5.0(@typescript-eslint/parser@6.5.0)(eslint@8.48.0)(typescript@5.2.2)
|
||||
@ -765,6 +776,33 @@ packages:
|
||||
resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==}
|
||||
dev: false
|
||||
|
||||
/@tanstack/match-sorter-utils@8.8.4:
|
||||
resolution: {integrity: sha512-rKH8LjZiszWEvmi01NR72QWZ8m4xmXre0OOwlRGnjU01Eqz/QnN+cqpty2PJ0efHblq09+KilvyR7lsbzmXVEw==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
remove-accents: 0.4.2
|
||||
dev: false
|
||||
|
||||
/@tanstack/query-core@4.33.0:
|
||||
resolution: {integrity: sha512-qYu73ptvnzRh6se2nyBIDHGBQvPY1XXl3yR769B7B6mIDD7s+EZhdlWHQ67JI6UOTFRaI7wupnTnwJ3gE0Mr/g==}
|
||||
dev: false
|
||||
|
||||
/@tanstack/vue-query@4.34.0(vue@3.3.4):
|
||||
resolution: {integrity: sha512-jXqvP5z+9mAuUVELH4A9j+KYhu84IpBFDGo60Z+9GV/td+CeDBLRoijBEixBg87SEilCCpJugc0yx2MOCwlwew==}
|
||||
peerDependencies:
|
||||
'@vue/composition-api': ^1.1.2
|
||||
vue: ^2.5.0 || ^3.0.0
|
||||
peerDependenciesMeta:
|
||||
'@vue/composition-api':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@tanstack/match-sorter-utils': 8.8.4
|
||||
'@tanstack/query-core': 4.33.0
|
||||
'@vue/devtools-api': 6.5.0
|
||||
vue: 3.3.4
|
||||
vue-demi: 0.13.11(vue@3.3.4)
|
||||
dev: false
|
||||
|
||||
/@types/eslint@8.44.2:
|
||||
resolution: {integrity: sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==}
|
||||
dependencies:
|
||||
@ -794,9 +832,14 @@ packages:
|
||||
'@types/lodash': 4.14.197
|
||||
dev: false
|
||||
|
||||
/@types/lodash.groupby@4.6.7:
|
||||
resolution: {integrity: sha512-dFUR1pqdMgjIBbgPJ/8axJX6M1C7zsL+HF4qdYMQeJ7XOp0Qbf37I3zh9gpXr/ks6tgEYPDRqyZRAnFYvewYHQ==}
|
||||
dependencies:
|
||||
'@types/lodash': 4.14.197
|
||||
dev: true
|
||||
|
||||
/@types/lodash@4.14.197:
|
||||
resolution: {integrity: sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g==}
|
||||
dev: false
|
||||
|
||||
/@types/markdown-it@12.2.3:
|
||||
resolution: {integrity: sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==}
|
||||
@ -805,6 +848,13 @@ packages:
|
||||
'@types/mdurl': 1.0.2
|
||||
dev: true
|
||||
|
||||
/@types/markdown-it@13.0.1:
|
||||
resolution: {integrity: sha512-SUEb8Frsxs3D5Gg9xek6i6EG6XQ5s+O+ZdQzIPESZVZw3Pv3CPQfjCJBI+RgqZd1IBeu18S0Rn600qpPnEK37w==}
|
||||
dependencies:
|
||||
'@types/linkify-it': 3.0.2
|
||||
'@types/mdurl': 1.0.2
|
||||
dev: true
|
||||
|
||||
/@types/mdurl@1.0.2:
|
||||
resolution: {integrity: sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==}
|
||||
dev: true
|
||||
@ -996,7 +1046,6 @@ packages:
|
||||
|
||||
/@vue/devtools-api@6.5.0:
|
||||
resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==}
|
||||
dev: true
|
||||
|
||||
/@vue/reactivity-transform@3.3.4:
|
||||
resolution: {integrity: sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==}
|
||||
@ -1237,7 +1286,6 @@ packages:
|
||||
|
||||
/argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
dev: true
|
||||
|
||||
/array-buffer-byte-length@1.0.0:
|
||||
resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==}
|
||||
@ -1700,7 +1748,6 @@ packages:
|
||||
/entities@3.0.1:
|
||||
resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==}
|
||||
engines: {node: '>=0.12'}
|
||||
dev: true
|
||||
|
||||
/entities@4.5.0:
|
||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||
@ -2732,7 +2779,6 @@ packages:
|
||||
resolution: {integrity: sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==}
|
||||
dependencies:
|
||||
uc.micro: 1.0.6
|
||||
dev: true
|
||||
|
||||
/lint-staged@14.0.1:
|
||||
resolution: {integrity: sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw==}
|
||||
@ -2875,7 +2921,6 @@ packages:
|
||||
linkify-it: 4.0.1
|
||||
mdurl: 1.0.1
|
||||
uc.micro: 1.0.6
|
||||
dev: true
|
||||
|
||||
/markdown-it@8.4.2:
|
||||
resolution: {integrity: sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==}
|
||||
@ -2947,7 +2992,6 @@ packages:
|
||||
|
||||
/mdurl@1.0.1:
|
||||
resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==}
|
||||
dev: true
|
||||
|
||||
/memoize-one@6.0.0:
|
||||
resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==}
|
||||
@ -3048,6 +3092,10 @@ packages:
|
||||
resolution: {integrity: sha512-PNxA/X8pWk+TiqPbsoIYH0GQ5Di7m6326/lwU/S4mlo4wGQddIcf/V//1f9TB0V4j59b57b+HZxt8h3iMROGvg==}
|
||||
dev: true
|
||||
|
||||
/moment@2.29.4:
|
||||
resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==}
|
||||
dev: false
|
||||
|
||||
/ms@2.1.2:
|
||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||
dev: true
|
||||
@ -3385,6 +3433,10 @@ packages:
|
||||
functions-have-names: 1.2.3
|
||||
dev: true
|
||||
|
||||
/remove-accents@0.4.2:
|
||||
resolution: {integrity: sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==}
|
||||
dev: false
|
||||
|
||||
/require-from-string@2.0.2:
|
||||
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -3970,7 +4022,6 @@ packages:
|
||||
|
||||
/uc.micro@1.0.6:
|
||||
resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==}
|
||||
dev: true
|
||||
|
||||
/unbox-primitive@1.0.2:
|
||||
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
|
||||
@ -4117,6 +4168,21 @@ packages:
|
||||
resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
|
||||
dev: true
|
||||
|
||||
/vue-demi@0.13.11(vue@3.3.4):
|
||||
resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
peerDependencies:
|
||||
'@vue/composition-api': ^1.0.0-rc.1
|
||||
vue: ^3.0.0-0 || ^2.6.0
|
||||
peerDependenciesMeta:
|
||||
'@vue/composition-api':
|
||||
optional: true
|
||||
dependencies:
|
||||
vue: 3.3.4
|
||||
dev: false
|
||||
|
||||
/vue-demi@0.14.6(vue@3.3.4):
|
||||
resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==}
|
||||
engines: {node: '>=12'}
|
||||
@ -4254,3 +4320,7 @@ packages:
|
||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
@ -1,12 +1,12 @@
|
||||
export function simpleLangName(code) {
|
||||
export function simpleLangName(code: string) {
|
||||
if (code === "all") {
|
||||
return "All";
|
||||
}
|
||||
const namesInEnglish = new Intl.DisplayNames(["en"], { type: "language" });
|
||||
return namesInEnglish.of(code);
|
||||
return namesInEnglish.of(code)!;
|
||||
}
|
||||
|
||||
export function langName(code) {
|
||||
export function langName(code: string) {
|
||||
if (code === "all") {
|
||||
return "All";
|
||||
}
|
||||
|
75
website/src/.vitepress/theme/components/DownloadButtons.vue
Normal file
75
website/src/.vitepress/theme/components/DownloadButtons.vue
Normal file
@ -0,0 +1,75 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import useReleaseQuery from "../queries/useReleaseQuery";
|
||||
|
||||
const {
|
||||
data: stableRelease,
|
||||
isLoading: isLoadingStable,
|
||||
isError: isErrorStable,
|
||||
error: errorStable
|
||||
} = useReleaseQuery("stable");
|
||||
|
||||
const {
|
||||
data: previewRelease,
|
||||
isLoading: isLoadingPreview,
|
||||
isError: isErrorPreview,
|
||||
error: errorPreview
|
||||
} = useReleaseQuery("preview");
|
||||
|
||||
const downloadInformation = computed(() => ({
|
||||
preview: {
|
||||
tagName: previewRelease.value?.tag_name,
|
||||
asset: (previewRelease.value?.assets ?? [])
|
||||
.find(a => /^tachiyomi-r\d{4,}.apk/.test(a.name)),
|
||||
},
|
||||
stable: {
|
||||
tagName: stableRelease.value?.tag_name?.slice(1),
|
||||
asset: (stableRelease.value?.assets ?? [])
|
||||
.find(a => /^tachiyomi-v\d+\.\d+\.\d+.apk/.test(a.name)),
|
||||
}
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="isLoadingStable || isLoadingPreview">
|
||||
This is the fancy loading indicator
|
||||
</div>
|
||||
<div v-else-if="isErrorStable">
|
||||
This is the error message: {{ errorStable }}
|
||||
</div>
|
||||
<div v-else-if="isErrorPreview">
|
||||
This is the error message: {{ errorPreview }}
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="buttons-wrapper">
|
||||
<a
|
||||
:download="downloadInformation.stable.asset?.name"
|
||||
:href="downloadInformation.stable.asset?.browser_download_url"
|
||||
>
|
||||
<span>Stable</span>
|
||||
<span>{{ downloadInformation.stable.tagName }}</span>
|
||||
</a>
|
||||
<a
|
||||
:download="downloadInformation.preview.asset?.name"
|
||||
:href="downloadInformation.preview.asset?.browser_download_url"
|
||||
>
|
||||
<span>Preview</span>
|
||||
<span>{{ downloadInformation.preview.tagName }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<p>
|
||||
Requires <strong>Android 6.0</strong> or higher.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="stylus">
|
||||
.buttons-wrapper
|
||||
display: flex
|
||||
gap: 2rem
|
||||
|
||||
& > a
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
</style>
|
@ -1,74 +1,22 @@
|
||||
<script>
|
||||
import axios from "axios";
|
||||
<script setup lang="ts">
|
||||
import groupBy from "lodash.groupby";
|
||||
import { GITHUB_EXTENSION_JSON } from "../../../config/constants";
|
||||
import { simpleLangName } from "../../../config/scripts/languages";
|
||||
import ExtensionFilters from "./ExtensionFilters.vue";
|
||||
import ExtensionList from "./ExtensionList.vue";
|
||||
import useExtensionsRepositoryQuery from "../../queries/useExtensionsRepositoryQuery";
|
||||
import { computed, onUpdated, ref } from 'vue';
|
||||
import type { Extension } from "../../queries/useExtensionsRepositoryQuery";
|
||||
|
||||
export default {
|
||||
components: { ExtensionList, ExtensionFilters },
|
||||
data() {
|
||||
return {
|
||||
extensions: [],
|
||||
filters: {
|
||||
const { data: extensions, isLoading } = useExtensionsRepositoryQuery();
|
||||
|
||||
const filters = ref({
|
||||
search: "",
|
||||
lang: [],
|
||||
lang: [] as string[],
|
||||
nsfw: "Show all",
|
||||
sort: "Ascending",
|
||||
},
|
||||
loading: true,
|
||||
observer: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
filteredExtensions() {
|
||||
const { extensions, filters } = this;
|
||||
});
|
||||
|
||||
const filtered = [];
|
||||
|
||||
for (const group of extensions) {
|
||||
let filteredGroup = filters.lang.length ? (filters.lang.includes(group[0].lang) ? group : []) : group;
|
||||
|
||||
if (filters.search) {
|
||||
filteredGroup = filteredGroup.filter(
|
||||
(ext) =>
|
||||
ext.name.toLowerCase().includes(filters.search.toLowerCase()) ||
|
||||
ext.sources.some((source) => source.id.includes(filters.search))
|
||||
);
|
||||
}
|
||||
filteredGroup = filteredGroup.filter((ext) =>
|
||||
filters.nsfw === "Show all" ? true : ext.nsfw === (filters.nsfw === "NSFW" ? 1 : 0)
|
||||
);
|
||||
|
||||
if (filters.sort && filters.sort === "Descending") {
|
||||
filteredGroup = filteredGroup.reverse();
|
||||
}
|
||||
if (filteredGroup.length) {
|
||||
filtered.push(filteredGroup);
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
},
|
||||
},
|
||||
async beforeMount() {
|
||||
const { data } = await axios.get(GITHUB_EXTENSION_JSON);
|
||||
const values = Object.values(groupBy(data, "lang"));
|
||||
values.sort(this.sortLanguages);
|
||||
this.$data.extensions = values;
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
updated() {
|
||||
if (window.location.hash) {
|
||||
window.location.replace(window.location.hash);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
sortLanguages(a, b) {
|
||||
function languageComparator(a: Extension[], b: Extension[]) {
|
||||
const langA = simpleLangName(a[0].lang);
|
||||
const langB = simpleLangName(b[0].lang);
|
||||
if (langA === "All" && langB === "English") {
|
||||
@ -90,13 +38,54 @@ export default {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const groupedExtensions = computed(() => {
|
||||
const values: Extension[][] = Object.values(groupBy(extensions.value, "lang"));
|
||||
values.sort(languageComparator)
|
||||
|
||||
return values
|
||||
})
|
||||
|
||||
const filteredExtensions = computed(() => {
|
||||
const filtered: Extension[][] = [];
|
||||
|
||||
for (const group of groupedExtensions.value) {
|
||||
let filteredGroup = filters.value.lang.length
|
||||
? (filters.value.lang.includes(group[0].lang) ? group : [])
|
||||
: group;
|
||||
|
||||
if (filters.value.search) {
|
||||
filteredGroup = filteredGroup.filter(
|
||||
(ext) =>
|
||||
ext.name.toLowerCase().includes(filters.value.search.toLowerCase()) ||
|
||||
ext.sources.some((source) => source.id.includes(filters.value.search))
|
||||
);
|
||||
}
|
||||
filteredGroup = filteredGroup.filter((ext) =>
|
||||
filters.value.nsfw === "Show all" ? true : ext.nsfw === (filters.value.nsfw === "NSFW" ? 1 : 0)
|
||||
);
|
||||
|
||||
if (filters.value.sort && filters.value.sort === "Descending") {
|
||||
filteredGroup = filteredGroup.reverse();
|
||||
}
|
||||
if (filteredGroup.length) {
|
||||
filtered.push(filteredGroup);
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
});
|
||||
|
||||
onUpdated(() => {
|
||||
if (window.location.hash) {
|
||||
window.location.replace(window.location.hash);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ExtensionFilters :extensions="extensions" @filters="filters = $event" />
|
||||
<div class="loading" v-if="loading" v-loading.lock="loading" style="min-height: 200px"></div>
|
||||
<ExtensionFilters :extensions="groupedExtensions" @filters="filters = $event" />
|
||||
<div class="loading" v-if="isLoading" v-loading.lock="isLoading" style="min-height: 200px"></div>
|
||||
<ExtensionList v-else :extensions="filteredExtensions" />
|
||||
</template>
|
||||
|
33
website/src/.vitepress/theme/components/ReleaseDate.vue
Normal file
33
website/src/.vitepress/theme/components/ReleaseDate.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, toRefs } from "vue";
|
||||
import moment from "moment";
|
||||
import useReleaseQuery from "../queries/useReleaseQuery";
|
||||
import type { ReleaseType } from "../queries/useReleaseQuery";
|
||||
|
||||
const props = defineProps<{ type: ReleaseType }>();
|
||||
const { type } = toRefs(props);
|
||||
|
||||
const { data: release, isLoading, isError } = useReleaseQuery(type);
|
||||
|
||||
const momentInfo = computed(() => {
|
||||
if (isLoading.value || isError.value || !release.value) {
|
||||
return {
|
||||
relative: "at an unknown time",
|
||||
exact: null,
|
||||
iso: null,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
relative: moment(release.value!.published_at).fromNow(),
|
||||
exact: moment(release.value!.published_at).format("dddd, MMMM Do YYYY [at] HH:mm"),
|
||||
iso: release.value!.published_at,
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<time :datetime="momentInfo.iso">
|
||||
{{ momentInfo.relative }}
|
||||
</time>
|
||||
</template>
|
52
website/src/.vitepress/theme/components/WhatsNew.vue
Normal file
52
website/src/.vitepress/theme/components/WhatsNew.vue
Normal file
@ -0,0 +1,52 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, toRefs } from "vue";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import useReleaseQuery from "../queries/useReleaseQuery";
|
||||
import type { ReleaseType } from "../queries/useReleaseQuery";
|
||||
|
||||
const props = defineProps<{ type: ReleaseType }>();
|
||||
const { type } = toRefs(props);
|
||||
|
||||
const { data: release, isLoading, isError, error } = useReleaseQuery(type);
|
||||
|
||||
const md = new MarkdownIt();
|
||||
|
||||
const whatsNew = computed(() => {
|
||||
const flavoredString = (release.value?.body ?? "")
|
||||
.replace(/(?<=\(|(, ))@(.*?)(?=\)|(, ))/g, "[@$2](https://github.com/$2)")
|
||||
|
||||
return md.render(flavoredString);
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="isLoading">
|
||||
This is the fancy loading indicator
|
||||
</div>
|
||||
<div v-else-if="isError">
|
||||
This is the error message: {{ error }}
|
||||
</div>
|
||||
<div v-else>
|
||||
<h2>What's new</h2>
|
||||
<div v-html="whatsNew"></div>
|
||||
<div>
|
||||
<p>
|
||||
View the full release
|
||||
<a href="https://github.com/tachiyomiorg/tachiyomi/releases/latest" target="_blank" rel="noopener">
|
||||
here
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="stylus">
|
||||
.buttons-wrapper
|
||||
display: flex
|
||||
gap: 2rem
|
||||
|
||||
& > a
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
</style>
|
@ -7,6 +7,8 @@ import ElementPlus from "element-plus";
|
||||
import "element-plus/dist/index.css";
|
||||
import "element-plus/theme-chalk/dark/css-vars.css";
|
||||
|
||||
import { VueQueryPlugin } from "@tanstack/vue-query"
|
||||
|
||||
import { enhanceAppWithTabs } from "vitepress-plugin-tabs/client";
|
||||
|
||||
export default {
|
||||
@ -18,6 +20,7 @@ export default {
|
||||
},
|
||||
enhanceApp({ app, router, siteData }) {
|
||||
app.use(ElementPlus);
|
||||
app.use(VueQueryPlugin);
|
||||
enhanceAppWithTabs(app);
|
||||
},
|
||||
};
|
||||
|
@ -0,0 +1,47 @@
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
import axios from "axios";
|
||||
import { GITHUB_EXTENSION_JSON } from "../../config/constants";
|
||||
|
||||
export type ReleaseType = "stable" | "preview";
|
||||
|
||||
export interface Extension {
|
||||
name: string
|
||||
pkg: string
|
||||
apk: string
|
||||
lang: string
|
||||
code: number
|
||||
version: string
|
||||
nsfw: number
|
||||
hasReadme: number
|
||||
hasChangelog: number
|
||||
sources: Source[]
|
||||
}
|
||||
|
||||
export interface Source {
|
||||
name: string
|
||||
lang: string
|
||||
id: string
|
||||
baseUrl: string
|
||||
versionId: number
|
||||
hasCloudflare: string
|
||||
}
|
||||
|
||||
export interface GitHubAsset {
|
||||
name: string
|
||||
content_type: string
|
||||
browser_download_url: string
|
||||
}
|
||||
|
||||
|
||||
export default function useExtensionsRepositoryQuery() {
|
||||
return useQuery({
|
||||
queryKey: ["extensions"],
|
||||
queryFn: async () => {
|
||||
const { data } = await axios.get<Extension[]>(GITHUB_EXTENSION_JSON);
|
||||
|
||||
return data;
|
||||
},
|
||||
initialData: () => [],
|
||||
refetchOnWindowFocus: false,
|
||||
})
|
||||
}
|
39
website/src/.vitepress/theme/queries/useReleaseQuery.ts
Normal file
39
website/src/.vitepress/theme/queries/useReleaseQuery.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
import axios from "axios";
|
||||
import { GITHUB_STABLE_API, GITHUB_PREVIEW_API } from "../../config/constants";
|
||||
import { unref, type Ref } from 'vue';
|
||||
|
||||
export type ReleaseType = "stable" | "preview";
|
||||
|
||||
export interface GitHubRelease {
|
||||
body: string
|
||||
tag_name: string
|
||||
name: string
|
||||
assets: GitHubAsset[]
|
||||
published_at: string
|
||||
}
|
||||
|
||||
export interface GitHubAsset {
|
||||
name: string
|
||||
content_type: string
|
||||
browser_download_url: string
|
||||
}
|
||||
|
||||
const apiUrls: Record<ReleaseType, string> = {
|
||||
stable: GITHUB_STABLE_API,
|
||||
preview: GITHUB_PREVIEW_API,
|
||||
};
|
||||
|
||||
export default function useReleaseQuery(type: ReleaseType | Ref<ReleaseType>) {
|
||||
return useQuery({
|
||||
queryKey: ["release", type],
|
||||
queryFn: async () => {
|
||||
const typeKey = unref(type);
|
||||
const { data } = await axios.get<GitHubRelease>(apiUrls[typeKey]);
|
||||
|
||||
return data;
|
||||
},
|
||||
initialData: () => null,
|
||||
refetchOnWindowFocus: false,
|
||||
})
|
||||
}
|
@ -3,8 +3,16 @@ title: Download
|
||||
description: Download page that allows users to access and install the latest version of the app.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
import DownloadButtons from "../.vitepress/theme/components/DownloadButtons.vue"
|
||||
import ReleaseDate from "../.vitepress/theme/components/ReleaseDate.vue";
|
||||
import WhatsNew from "../.vitepress/theme/components/WhatsNew.vue"
|
||||
</script>
|
||||
|
||||
# Download
|
||||
|
||||
Download page that allows users to access and install the latest version of the app.
|
||||
Download the latest stable version of **Tachiyomi** that released <ReleaseDate type="stable" /> or the preview version that released <ReleaseDate type="preview" />.
|
||||
|
||||
![GitHub all releases](https://img.shields.io/github/downloads/tachiyomiorg/tachiyomi/total?label=downloads&labelColor=27303D&color=0D1117&logo=github&logoColor=FFFFFF&style=flat)
|
||||
<DownloadButtons />
|
||||
|
||||
<WhatsNew type="stable" />
|
||||
|
Loading…
Reference in New Issue
Block a user