feat: ApkTable

This commit is contained in:
lisonge 2024-10-15 15:48:00 +08:00
parent 4e78aeff39
commit 06cc806834
10 changed files with 183 additions and 109 deletions

View File

@ -1,84 +0,0 @@
<script setup lang="ts">
import { computed, onMounted, shallowRef } from 'vue';
const props = withDefaults(
defineProps<{
beta?: boolean;
}>(),
{},
);
const apkUrl = shallowRef('');
const apkName = computed(() =>
(apkUrl.value.split('/').at(-1) || '').replace('.wasm', '.apk'),
);
const loading = shallowRef(false);
const downloadApk = async () => {
if (!apkUrl.value || loading.value) return;
loading.value = true;
try {
const file = await fetch(apkUrl.value)
.then((r) => r.arrayBuffer())
.then(
(b) =>
new File([b], apkName.value, {
type: 'application/vnd.android.package-archive',
}),
);
const { saveAs } = await import('file-saver');
saveAs(file, apkName.value);
} finally {
loading.value = false;
}
};
// preload file-saver in browser
if (globalThis.document) {
import('file-saver');
}
const pkg = computed(() => (props.beta ? 'app-beta' : 'app'));
const key = computed(() => `apkUrl-${pkg.value}`);
onMounted(async () => {
apkUrl.value = localStorage.getItem(key.value) || '';
const versionUrl = `https://registry.npmmirror.com/@gkd-kit/${pkg.value}/latest/files`;
const r = await fetch(versionUrl);
const data = await r.json();
apkUrl.value = new URL(data.downloadUrl, r.url).href;
localStorage.setItem(key.value, apkUrl.value);
});
</script>
<template>
<div inline-flex items-center gap-10px>
<a
:class="{
'cursor-progress': loading,
'opacity-50': loading,
'cursor-pointer': !loading,
}"
@click="downloadApk"
>
{{ apkName }}
</a>
<svg v-if="loading" animate-spin size-16px viewBox="0 0 16 16" fill="none">
<circle
cx="8"
cy="8"
r="7"
stroke="currentColor"
stroke-opacity="0.25"
stroke-width="2"
vector-effect="non-scaling-stroke"
></circle>
<path
d="M15 8a7.002 7.002 0 00-7-7"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
vector-effect="non-scaling-stroke"
></path>
</svg>
</div>
</template>

View File

@ -0,0 +1,34 @@
<script setup lang="ts">
import DownloadText from './DownloadText.vue';
import { data } from '../data/apk.data';
</script>
<template>
<table>
<thead>
<tr>
<th>版本</th>
<th>下载链接</th>
<th>更新日期</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>正式版</td>
<td>
<DownloadText :href="data.stable.href" :name="data.stable.filename" />
</td>
<td>{{ data.stable.date }}</td>
<td>稳定版</td>
</tr>
<tr>
<td>测试版</td>
<td>
<DownloadText :href="data.beta.href" :name="data.beta.filename" />
</td>
<td>{{ data.beta.date }}</td>
<td>更新快不稳定</td>
</tr>
</tbody>
</table>
</template>

View File

@ -0,0 +1,88 @@
<script setup lang="ts">
import { computed, shallowRef } from 'vue';
const props = withDefaults(
defineProps<{
href: string;
name?: string;
type?: string;
}>(),
{
type: 'application/vnd.android.package-archive',
},
);
const filename = computed(() => {
if (props.name) return props.name;
return props.href.split('/').at(-1)!;
});
const loading = shallowRef(false);
const download = async () => {
if (loading.value) return;
loading.value = true;
import('file-saver');
try {
const file = await fetch(props.href)
.then((r) => r.arrayBuffer())
.then((b) => {
return new File([b], filename.value, {
type: props.type,
});
});
const { saveAs } = await import('file-saver');
saveAs(file, filename.value);
} finally {
loading.value = false;
}
};
</script>
<template>
<a
relative
:class="{
'cursor-progress': loading,
'cursor-pointer': !loading,
}"
@click="download"
:data-href="href"
>
<span
:class="{
'opacity-50': loading,
}"
>
{{ filename }}
</span>
<div
v-if="loading"
pointer-events-none
absolute
left="1/1"
top-0
bottom-0
flex
items-center
pl-4px
>
<svg animate-spin size-16px viewBox="0 0 16 16" fill="none">
<circle
cx="8"
cy="8"
r="7"
stroke="currentColor"
stroke-opacity="0.25"
stroke-width="2"
vector-effect="non-scaling-stroke"
></circle>
<path
d="M15 8a7.002 7.002 0 00-7-7"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
vector-effect="non-scaling-stroke"
></path>
</svg>
</div>
</a>
</template>

View File

@ -1,13 +1,13 @@
import naiveComponents from './naive'; import ApkTable from './ApkTable.vue';
import GImg from './GImg'; import GImg from './GImg';
import ImageTable from './ImageTable.vue';
import IdentifierField from './IdentifierField.vue'; import IdentifierField from './IdentifierField.vue';
import ApkDownloadButton from './ApkDownloadButton.vue'; import ImageTable from './ImageTable.vue';
import naiveComponents from './naive';
export default { export default {
...naiveComponents, ...naiveComponents,
GImg, GImg,
ImageTable, ImageTable,
IdentifierField, IdentifierField,
ApkDownloadButton ApkTable,
}; };

View File

@ -0,0 +1,39 @@
export interface VersionInfo {
name: string;
date: string;
href: string;
filename: string;
}
export interface ApkData {
stable: VersionInfo;
beta: VersionInfo;
}
const getVersionInfo = async (url: string): Promise<VersionInfo> => {
const r = await fetch(url).then((r) => r.json());
return {
name: r.versionName,
href: new URL(r.downloadUrl, url).href,
date: String(r.date || '').substring(0, 10),
filename: 'gkd-v' + r.versionName + '.apk',
};
};
const stableRelease = await getVersionInfo(
'https://registry.npmmirror.com/@gkd-kit/app/latest/files/index.json',
);
const betaRelease = await getVersionInfo(
'https://registry.npmmirror.com/@gkd-kit/app-beta/latest/files/index.json',
);
const load = async (): Promise<ApkData> => {
return {
stable: stableRelease,
beta: betaRelease,
};
};
export default {
load,
};
export declare const data: ApkData;

View File

@ -0,0 +1,12 @@
const load = async (): Promise<string> => {
const version = await fetch(
'https://registry.npmmirror.com/@gkd-kit/assets/latest/files/package.json',
).then((r) => r.json().then((j) => j.version as string));
return `https://registry.npmmirror.com/@gkd-kit/assets/${version}/files/assets/`;
};
export default {
load,
};
export declare const data: string;

View File

@ -1,3 +1 @@
/// <reference types="vitepress/client" /> /// <reference types="vitepress/client" />
declare const ASSETS_VERSION: string

View File

@ -1,14 +1,12 @@
const mirrorHost = () => { import { data as mirrorHost } from '../data/mirror.data';
return `https://registry.npmmirror.com/@gkd-kit/assets/${ASSETS_VERSION}/files/assets/`;
};
const imgHost = 'https://a.gkd.li/'; const imgHost = 'https://a.gkd.li/';
export const convertSrc = (name: string): string => { export const convertSrc = (name: string): string => {
if (name && name.startsWith('https:')) { if (name && name.startsWith('https:')) {
if (name.startsWith(imgHost)) { if (name.startsWith(imgHost)) {
return mirrorHost() + name.slice(imgHost.length); return mirrorHost + name.slice(imgHost.length);
} }
return name; return name;
} }
return mirrorHost() + name; return mirrorHost + name;
}; };

View File

@ -2,10 +2,7 @@
## 安装 {#install} ## 安装 {#install}
| 版本 | 下载链接 | 更新日期 | 备注 | <ApkTable />
| ------ | -------------------------- | ---------- | ------------ |
| 正式版 | <ApkDownloadButton /> | 2024-08-27 | 稳定版 |
| 测试版 | <ApkDownloadButton beta /> | 2024-10-03 | 更新快不稳定 |
或前往 [Github Releases](https://github.com/gkd-kit/gkd/releases/latest) 下载 或前往 [Github Releases](https://github.com/gkd-kit/gkd/releases/latest) 下载

View File

@ -1,17 +1,9 @@
import { defineConfig } from 'vite';
import unocss from 'unocss/vite'; import unocss from 'unocss/vite';
import { defineConfig } from 'vite';
import { mirror } from './.vitepress/plugins'; import { mirror } from './.vitepress/plugins';
const ASSETS_VERSION = await fetch(
'https://registry.npmmirror.com/@gkd-kit/assets/latest/files/package.json',
).then((r) => r.json().then((j) => j.version as string));
export default defineConfig({ export default defineConfig({
define: { ASSETS_VERSION: JSON.stringify(ASSETS_VERSION) }, plugins: [unocss(), mirror()],
plugins: [
unocss(),
mirror(),
],
server: { server: {
host: '127.0.0.1', host: '127.0.0.1',
port: 8633, port: 8633,