forked from didirus/AstralRinth
* update trivial dependencies * switch to sha1_smol * update async_zip * fix cli * clippy & fmt * js lints * fix build for ci
290 lines
6.3 KiB
Vue
290 lines
6.3 KiB
Vue
<script setup>
|
|
import {
|
|
Card,
|
|
Avatar,
|
|
Button,
|
|
formatNumber,
|
|
formatCategory,
|
|
DownloadIcon,
|
|
HeartIcon,
|
|
CalendarIcon,
|
|
} from 'omorphia'
|
|
import { computed, ref } from 'vue'
|
|
import dayjs from 'dayjs'
|
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
|
import { useRouter } from 'vue-router'
|
|
import { useFetch } from '@/helpers/fetch.js'
|
|
import { list } from '@/helpers/profile.js'
|
|
import { handleError } from '@/store/notifications.js'
|
|
import { install as pack_install } from '@/helpers/pack.js'
|
|
dayjs.extend(relativeTime)
|
|
|
|
const router = useRouter()
|
|
const installing = ref(false)
|
|
|
|
const props = defineProps({
|
|
project: {
|
|
type: Object,
|
|
default() {
|
|
return {}
|
|
},
|
|
},
|
|
confirmModal: {
|
|
type: Object,
|
|
default() {
|
|
return {}
|
|
},
|
|
},
|
|
modInstallModal: {
|
|
type: Object,
|
|
default() {
|
|
return {}
|
|
},
|
|
},
|
|
})
|
|
|
|
const toColor = computed(() => {
|
|
let color = props.project.color
|
|
|
|
color >>>= 0
|
|
const b = color & 0xff
|
|
const g = (color >>> 8) & 0xff
|
|
const r = (color >>> 16) & 0xff
|
|
return 'rgba(' + [r, g, b, 1].join(',') + ')'
|
|
})
|
|
|
|
const toTransparent = computed(() => {
|
|
let color = props.project.color
|
|
|
|
color >>>= 0
|
|
const b = color & 0xff
|
|
const g = (color >>> 8) & 0xff
|
|
const r = (color >>> 16) & 0xff
|
|
return (
|
|
'linear-gradient(rgba(' +
|
|
[r, g, b, 0.03].join(',') +
|
|
'), 65%, rgba(' +
|
|
[r, g, b, 0.3].join(',') +
|
|
'))'
|
|
)
|
|
})
|
|
|
|
const install = async (e) => {
|
|
e?.stopPropagation()
|
|
installing.value = true
|
|
const versions = await useFetch(
|
|
`https://api.modrinth.com/v2/project/${props.project.project_id}/version`,
|
|
'project versions',
|
|
)
|
|
|
|
if (props.project.project_type === 'modpack') {
|
|
const packs = Object.values(await list(true).catch(handleError))
|
|
|
|
if (
|
|
packs.length === 0 ||
|
|
!packs
|
|
.map((value) => value.metadata)
|
|
.find((pack) => pack.linked_data?.project_id === props.project.project_id)
|
|
) {
|
|
installing.value = true
|
|
await pack_install(
|
|
props.project.project_id,
|
|
versions[0].id,
|
|
props.project.title,
|
|
props.project.icon_url,
|
|
).catch(handleError)
|
|
installing.value = false
|
|
} else
|
|
props.confirmModal.show(
|
|
props.project.project_id,
|
|
versions[0].id,
|
|
props.project.title,
|
|
props.project.icon_url,
|
|
)
|
|
} else {
|
|
props.modInstallModal.show(props.project.project_id, versions)
|
|
}
|
|
|
|
installing.value = false
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="wrapper">
|
|
<Card class="project-card button-base" @click="router.push(`/project/${project.slug}`)">
|
|
<div
|
|
class="banner"
|
|
:style="{
|
|
'background-color': project.featured_gallery ?? project.gallery[0] ? null : toColor,
|
|
'background-image': `url(${
|
|
project.featured_gallery ??
|
|
project.gallery[0] ??
|
|
'https://launcher-files.modrinth.com/assets/maze-bg.png'
|
|
})`,
|
|
'no-image': !project.featured_gallery && !project.gallery[0],
|
|
}"
|
|
>
|
|
<div class="badges">
|
|
<div class="badge">
|
|
<DownloadIcon />
|
|
{{ formatNumber(project.downloads) }}
|
|
</div>
|
|
<div class="badge">
|
|
<HeartIcon />
|
|
{{ formatNumber(project.follows) }}
|
|
</div>
|
|
<div class="badge">
|
|
<CalendarIcon />
|
|
{{ formatCategory(dayjs(project.date_modified).fromNow()) }}
|
|
</div>
|
|
</div>
|
|
<div
|
|
class="badges-wrapper"
|
|
:class="{
|
|
'no-image': !project.featured_gallery && !project.gallery[0],
|
|
}"
|
|
:style="{
|
|
background: !project.featured_gallery && !project.gallery[0] ? toTransparent : null,
|
|
}"
|
|
></div>
|
|
</div>
|
|
<Avatar class="icon" size="sm" :src="project.icon_url" />
|
|
<div class="title">
|
|
<div class="title-text">
|
|
{{ project.title }}
|
|
</div>
|
|
<div class="author">by {{ project.author }}</div>
|
|
</div>
|
|
<div class="description">
|
|
{{ project.description }}
|
|
</div>
|
|
</Card>
|
|
<Button color="primary" class="install" :disabled="installing" @click="install">
|
|
<DownloadIcon />
|
|
{{ installing ? 'Installing' : 'Install' }}
|
|
</Button>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.wrapper {
|
|
position: relative;
|
|
aspect-ratio: 1;
|
|
|
|
&:hover {
|
|
.install:enabled {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
.project-card {
|
|
display: grid;
|
|
grid-gap: 1rem;
|
|
grid-template:
|
|
'. . . .' 0
|
|
'. icon title .' 3rem
|
|
'banner banner banner banner' auto
|
|
'. description description .' 3.5rem
|
|
'. . . .' 0 / 0 3rem minmax(0, 1fr) 0;
|
|
max-width: 100%;
|
|
height: 100%;
|
|
padding: 0;
|
|
margin: 0;
|
|
|
|
.icon {
|
|
grid-area: icon;
|
|
}
|
|
|
|
.title {
|
|
max-width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
grid-area: title;
|
|
white-space: nowrap;
|
|
|
|
.title-text {
|
|
width: 100%;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
font-size: var(--font-size-md);
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
|
|
.author {
|
|
font-size: var(--font-size-sm);
|
|
grid-area: author;
|
|
}
|
|
|
|
.banner {
|
|
grid-area: banner;
|
|
background-size: cover;
|
|
background-position: center;
|
|
position: relative;
|
|
|
|
.badges-wrapper {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
mix-blend-mode: hard-light;
|
|
}
|
|
|
|
.badges {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
padding: var(--gap-sm);
|
|
gap: var(--gap-xs);
|
|
display: flex;
|
|
z-index: 10;
|
|
flex-direction: row;
|
|
justify-content: flex-end;
|
|
align-items: flex-end;
|
|
}
|
|
}
|
|
|
|
.description {
|
|
grid-area: description;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 3;
|
|
-webkit-box-orient: vertical;
|
|
}
|
|
}
|
|
|
|
.badge {
|
|
background-color: var(--color-raised-bg);
|
|
font-size: var(--font-size-xs);
|
|
padding: var(--gap-xs) var(--gap-sm);
|
|
border-radius: var(--radius-sm);
|
|
|
|
svg {
|
|
width: 1rem;
|
|
height: 1rem;
|
|
margin-right: var(--gap-xs);
|
|
}
|
|
}
|
|
|
|
.install {
|
|
position: absolute;
|
|
top: calc(5rem + var(--gap-sm));
|
|
right: var(--gap-sm);
|
|
z-index: 10;
|
|
opacity: 0;
|
|
transition: opacity 0.2s ease-in-out;
|
|
|
|
svg {
|
|
width: 1rem;
|
|
height: 1rem;
|
|
}
|
|
}
|
|
</style>
|