mirror of
https://github.com/HangarMC/Hangar.git
synced 2024-12-21 06:51:19 +08:00
download button, set recommended button
This commit is contained in:
parent
7c47ca0969
commit
34b12c5269
@ -3,7 +3,6 @@ import { Menu, MenuButton, MenuItems } from "@headlessui/vue";
|
||||
import IconMdiMenuDown from "~icons/mdi/menu-down";
|
||||
import IconMdiMenuUp from "~icons/mdi/menu-up";
|
||||
import Button from "~/components/design/Button.vue";
|
||||
import ServerOnly from "~/components/design/Tooltip.vue";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
@ -2,6 +2,17 @@
|
||||
import Popper from "vue3-popper";
|
||||
import { defineComponent, onMounted, ref } from "vue";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
hover?: boolean;
|
||||
show?: boolean | null;
|
||||
}>(),
|
||||
{
|
||||
hover: true,
|
||||
show: null,
|
||||
}
|
||||
);
|
||||
|
||||
const ServerOnly = defineComponent({
|
||||
name: "ServerOnly",
|
||||
setup(_, { slots }) {
|
||||
@ -16,7 +27,7 @@ const ServerOnly = defineComponent({
|
||||
|
||||
<template>
|
||||
<ClientOnly>
|
||||
<Popper v-bind="$attrs" hover open-delay="200" close-delay="100">
|
||||
<Popper v-bind="$attrs" :hover="hover" open-delay="200" close-delay="100" :show="show">
|
||||
<slot />
|
||||
<template #content="props">
|
||||
<slot name="content" v-bind="props" />
|
||||
|
@ -16,6 +16,11 @@ function open() {
|
||||
function close() {
|
||||
isOpen.value = false;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open: open,
|
||||
close: close,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
157
frontend-new/src/components/projects/DownloadButton.vue
Normal file
157
frontend-new/src/components/projects/DownloadButton.vue
Normal file
@ -0,0 +1,157 @@
|
||||
<script lang="ts" setup>
|
||||
import Button from "~/components/design/Button.vue";
|
||||
import Tooltip from "~/components/design/Tooltip.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { HangarProject, HangarVersion, IPlatform } from "hangar-internal";
|
||||
import { computed, ref } from "vue";
|
||||
import { Platform } from "~/types/enums";
|
||||
import DropdownButton from "~/components/design/DropdownButton.vue";
|
||||
import { useBackendDataStore } from "~/store/backendData";
|
||||
import DropdownItem from "~/components/design/DropdownItem.vue";
|
||||
import { useInternalApi } from "~/composables/useApi";
|
||||
import Modal from "~/components/modals/Modal.vue";
|
||||
import Alert from "~/components/design/Alert.vue";
|
||||
|
||||
const i18n = useI18n();
|
||||
const backendData = useBackendDataStore();
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
project: HangarProject;
|
||||
copyButton?: boolean;
|
||||
platformSelection?: boolean;
|
||||
small?: boolean;
|
||||
version?: HangarVersion;
|
||||
platform?: IPlatform;
|
||||
}>(),
|
||||
{
|
||||
copyButton: true,
|
||||
platformSelection: false,
|
||||
small: true,
|
||||
}
|
||||
);
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
const selectedPlatform = ref<Platform | null>(null);
|
||||
const copySuccessful = ref<boolean>(false);
|
||||
const token = ref<string | null>(null);
|
||||
const confirmModal = ref<Modal>(null);
|
||||
|
||||
if (props.platformSelection) {
|
||||
const keys = props.project.recommendedVersions ? Object.keys(props.project.recommendedVersions) : [];
|
||||
selectedPlatform.value = keys.length > 0 ? Platform[keys[0] as keyof typeof Platform] : Platform.PAPER;
|
||||
}
|
||||
|
||||
const external = computed(() =>
|
||||
props.platformSelection ? props.project.recommendedVersions[selectedPlatform.value!] !== null : props.version!.externalUrl !== null
|
||||
);
|
||||
const externalUrl = computed(() => (props.platformSelection ? props.project.recommendedVersions[selectedPlatform.value!] : props.version!.externalUrl));
|
||||
|
||||
function copyDownloadUrl() {
|
||||
let url;
|
||||
if (external.value) {
|
||||
url = externalUrl.value;
|
||||
} else {
|
||||
const versionString = props.platformSelection ? "recommended" : props.version!.name;
|
||||
const platform = props.platformSelection ? selectedPlatform.value : props.platform!.name;
|
||||
url = `${window.location.protocol}//${window.location.host}/api/v1/projects/${props.project.namespace.owner}/${props.project.namespace.slug}/versions/${versionString}/${platform}/download`;
|
||||
}
|
||||
if (url) {
|
||||
navigator.clipboard.writeText(url);
|
||||
copySuccessful.value = true;
|
||||
setTimeout(() => (copySuccessful.value = false), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
async function checkAndDownload() {
|
||||
if (await requiresConfirmation()) {
|
||||
confirmModal.value?.open();
|
||||
return;
|
||||
}
|
||||
return download();
|
||||
}
|
||||
|
||||
function download(): Promise<any> {
|
||||
if (external.value) {
|
||||
confirmModal.value?.close();
|
||||
return Promise.resolve(window.open(externalUrl.value || "", "_blank"));
|
||||
}
|
||||
const versionString = props.platformSelection ? "recommended" : props.version!.name;
|
||||
const platform = props.platformSelection ? selectedPlatform.value : props.platform!.name;
|
||||
window.open(
|
||||
`/api/internal/versions/version/${props.project.namespace.owner}/${props.project.namespace.slug}/versions/${versionString}/${platform}/download?token=${token.value}`,
|
||||
"_blank"
|
||||
);
|
||||
token.value = null;
|
||||
confirmModal.value?.close();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
async function requiresConfirmation() {
|
||||
if (token.value != null) {
|
||||
return true;
|
||||
}
|
||||
if (external.value) {
|
||||
return false;
|
||||
}
|
||||
loading.value = true;
|
||||
const versionString = props.platformSelection ? "recommended" : props.version!.name;
|
||||
const platform = props.platformSelection ? selectedPlatform.value : props.platform!.name.toLowerCase();
|
||||
try {
|
||||
await useInternalApi(
|
||||
`versions/version/${props.project.namespace.owner}/${props.project.namespace.slug}/versions/${versionString}/${platform}/downloadCheck`
|
||||
)
|
||||
.then(() => (loading.value = false))
|
||||
.catch((err) => {
|
||||
if (err.response && err.response.status === 428) {
|
||||
token.value = err.response.data;
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
loading.value = false;
|
||||
return false;
|
||||
} catch (ex) {
|
||||
loading.value = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- todo make this actually look nice -->
|
||||
<div class="flex">
|
||||
<DropdownButton v-if="platformSelection" :button-size="small ? 'small' : 'large'">
|
||||
<template #button-label> <IconMdiAlertOutline /><!-- todo platform icons --> </template>
|
||||
<DropdownItem v-for="(pl, i) in Object.keys(project.recommendedVersions)" :key="i" class="flex items-center" @click="selectedPlatform = pl">
|
||||
<IconMdiAlertOutline class="mr-1" /><!-- todo platform icons -->
|
||||
{{ backendData.platforms.get(pl).name }}
|
||||
</DropdownItem>
|
||||
</DropdownButton>
|
||||
|
||||
<Button :size="small ? 'small' : 'large'" :loading="loading" @click="checkAndDownload">
|
||||
<IconMdiDownloadOutline />
|
||||
{{ external ? i18n.t("version.page.downloadExternal") : i18n.t("version.page.download") }}
|
||||
</Button>
|
||||
|
||||
<Modal ref="confirmModal" :title="i18n.t('version.page.confirmation.title', [project.name, version ? version.name : '', project.owner.name])">
|
||||
<Alert type="danger" class="my-2">
|
||||
{{ i18n.t("version.page.confirmation.alert") }}
|
||||
</Alert>
|
||||
<em>{{ i18n.t("version.page.confirmation.disclaimer") }}</em>
|
||||
|
||||
<div class="mt-2 flex flex-wrap gap-2">
|
||||
<Button button-type="secondary" @click="confirmModal?.close()">{{ i18n.t("version.page.confirmation.deny") }}</Button>
|
||||
<Button button-type="red" @click="download">{{ i18n.t("version.page.confirmation.agree") }}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<Tooltip v-if="copyButton" :hover="false" :show="copySuccessful">
|
||||
<template #content>
|
||||
<span>{{ i18n.t("version.page.downloadUrlCopied") }}</span>
|
||||
</template>
|
||||
<Button :size="small ? 'small' : 'large'" @click="copyDownloadUrl">
|
||||
<IconMdiContentCopy />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</template>
|
@ -19,6 +19,7 @@ import { NamedPermission, Visibility } from "~/types/enums";
|
||||
import Markdown from "~/components/Markdown.vue";
|
||||
import { AxiosError } from "axios";
|
||||
import { useRouter } from "vue-router";
|
||||
import DownloadButton from "~/components/projects/DownloadButton.vue";
|
||||
|
||||
const ctx = useContext();
|
||||
const i18n = useI18n();
|
||||
@ -117,8 +118,12 @@ async function sendForApproval() {
|
||||
<p>{{ project.description }}</p>
|
||||
</div>
|
||||
<div class="flex sm:flex-col space-y-2 items-end justify-between sm:justify-around flex-shrink-0">
|
||||
<!-- TODO: download button component with functionality -->
|
||||
<Button size="large">Download latest</Button>
|
||||
<DownloadButton
|
||||
v-if="project.recommendedVersions && Object.keys(project.recommendedVersions).length > 0"
|
||||
:project="project"
|
||||
:platform-selection="true"
|
||||
:small="false"
|
||||
/>
|
||||
<div class="flex">
|
||||
<Tooltip>
|
||||
<template #content>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { NamedPermission, Platform, ReviewState } from "~/types/enums";
|
||||
import { NamedPermission, Platform, ReviewState, Visibility } from "~/types/enums";
|
||||
import { HangarProject, HangarVersion, IPlatform } from "hangar-internal";
|
||||
import { computed, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
@ -26,6 +26,8 @@ import DropdownButton from "~/components/design/DropdownButton.vue";
|
||||
import DropdownItem from "~/components/design/DropdownItem.vue";
|
||||
import PlatformVersionEditModal from "~/components/modals/PlatformVersionEditModal.vue";
|
||||
import { AxiosError } from "axios";
|
||||
import Tooltip from "~/components/design/Tooltip.vue";
|
||||
import DownloadButton from "~/components/projects/DownloadButton.vue";
|
||||
|
||||
const route = useRoute();
|
||||
const i18n = useI18n();
|
||||
@ -85,7 +87,6 @@ async function savePage(content: string) {
|
||||
}
|
||||
|
||||
async function setRecommended() {
|
||||
//this.loading.recommend = true;
|
||||
try {
|
||||
await useInternalApi(`versions/version/${props.project.id}/${projectVersion.value?.id}/${platform.value?.enumName}/recommend`, true, "post");
|
||||
notification.success(i18n.t("version.success.recommended", [platform.value?.name]));
|
||||
@ -93,7 +94,6 @@ async function setRecommended() {
|
||||
} catch (e) {
|
||||
handleRequestError(e as AxiosError, ctx, i18n);
|
||||
}
|
||||
// this.loading.recommend = false;
|
||||
}
|
||||
|
||||
async function deleteVersion(comment: string) {
|
||||
@ -153,13 +153,30 @@ async function restoreVersion() {
|
||||
<em v-if="hasPerms(NamedPermission.REVIEWER) && projectVersion.approvedBy" class="ml-2 text-lg">
|
||||
{{ i18n.t("version.page.adminMsg", [projectVersion.approvedBy, i18n.d(projectVersion.createdAt, "date")]) }}
|
||||
</em>
|
||||
<!-- todo set recommended -->
|
||||
|
||||
<!-- todo delete -->
|
||||
<!-- todo download -->
|
||||
<!-- todo admin actions -->
|
||||
</div>
|
||||
<div class="flex-grow"></div>
|
||||
<div class="inline-flex items-center">
|
||||
<DownloadButton :small="true" :version="projectVersion" :project="project" :platform="platform" class="mr-2" />
|
||||
|
||||
<Tooltip
|
||||
v-if="
|
||||
hasPerms(NamedPermission.EDIT_VERSION) &&
|
||||
projectVersion.visibility !== Visibility.SOFT_DELETE &&
|
||||
!projectVersion.recommended.includes(platform?.enumName)
|
||||
"
|
||||
>
|
||||
<template #content>
|
||||
<span>{{ i18n.t("version.page.setRecommendedTooltip", [platform?.name]) }}</span>
|
||||
</template>
|
||||
<Button size="small" class="mr-2" @click="setRecommended">
|
||||
<IconMdiDiamond />
|
||||
{{ i18n.t("version.page.setRecommended") }}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<!-- todo Make these nicer/put somewhere else -->
|
||||
<template v-if="hasPerms(NamedPermission.REVIEWER)">
|
||||
<Button v-if="isReviewStateChecked" color="success" :to="route.path + '/reviews'">
|
||||
|
Loading…
Reference in New Issue
Block a user