Update master with new auth (#1236)

* Begin UI for threads and moderation overhaul

* Hide close button on non-report threads

* Fix review age coloring

* Add project count

* Remove action buttons from queue page and add queued date to project page

* Hook up to actual data

* Remove unused icon

* Get up to 1000 projects in queue

* prettier

* more prettier

* Changed all the things

* lint

* rebuild

* Add omorphia

* Workaround formatjs bug in ThreadSummary.vue

* Fix notifications page on prod

* Fix a few notifications and threads bugs

* lockfile

* Fix duplicate button styles

* more fixes and polishing

* More fixes

* Remove legacy pages

* More bugfixes

* Add some error catching for reports and notifications

* More error handling

* fix lint

* Add inbox links

* Remove loading component and rename member header

* Rely on threads always existing

* Handle if project update notifs are not grouped

* oops

* Fix chips on notifications page

* Import ModalModeration

* finish threads

* New authentication (#1234)

* Initial new auth work

* more auth pages

* Finish most

* more

* fix on landing page

* Finish everything but PATs + Sessions

* fix threads merge bugs

* fix cf pages ssr

* fix most issues

* Finish authentication

* Fix merge

---------

Co-authored-by: triphora <emma@modrinth.com>
Co-authored-by: Jai A <jaiagr+gpg@pm.me>
Co-authored-by: Geometrically <18202329+Geometrically@users.noreply.github.com>
This commit is contained in:
Prospector
2023-07-20 11:19:42 -07:00
committed by GitHub
parent a5613ebb10
commit 34d63f3557
72 changed files with 2373 additions and 711 deletions

View File

@@ -106,6 +106,8 @@
:toggle-collapsed="() => (collapsedChecklist = !collapsedChecklist)"
:all-members="allMembers"
:update-members="updateMembers"
:auth="auth"
:tags="tags"
/>
<NuxtPage
v-model:project="project"
@@ -149,7 +151,7 @@
/>
</Head>
<ModalModeration
v-if="$auth.user"
v-if="auth.user"
ref="modalModeration"
:project="project"
:status="moderationStatus"
@@ -161,7 +163,7 @@
</div>
</Modal>
<ModalReport
v-if="$auth.user"
v-if="auth.user"
ref="modal_project_report"
:item-id="project.id"
item-type="project"
@@ -169,7 +171,7 @@
<div
:class="{
'normal-page': true,
'alt-layout': $cosmetics.projectLayout,
'alt-layout': cosmetics.projectLayout,
}"
>
<div class="normal-page__sidebar">
@@ -229,7 +231,7 @@
class="categories"
>
<Badge
v-if="$auth.user && currentMember"
v-if="auth.user && currentMember"
:type="project.status"
class="status-badge"
/>
@@ -287,7 +289,7 @@
</div>
<hr class="card-divider" />
<div class="input-group">
<template v-if="$auth.user">
<template v-if="auth.user">
<button class="iconified-button" @click="$refs.modal_project_report.show()">
<ReportIcon aria-hidden="true" />
Report
@@ -344,7 +346,7 @@
/>
<div class="buttons status-buttons">
<button
v-if="$tag.approvedStatuses.includes(project.status)"
v-if="tags.approvedStatuses.includes(project.status)"
class="iconified-button"
@click="clearMessage"
>
@@ -354,14 +356,14 @@
</div>
</div>
<div
v-if="$auth.user && $tag.staffRoles.includes($auth.user.role)"
v-if="auth.user && tags.staffRoles.includes(auth.user.role)"
class="universal-card moderation-card"
>
<h2>Moderation actions</h2>
<div class="input-stack">
<button
v-if="
!$tag.approvedStatuses.includes(project.status) || project.status === 'processing'
!tags.approvedStatuses.includes(project.status) || project.status === 'processing'
"
class="iconified-button brand-button"
@click="openModerationModal(requestedStatus)"
@@ -372,9 +374,9 @@
</button>
<button
v-if="
$tag.approvedStatuses.includes(project.status) ||
tags.approvedStatuses.includes(project.status) ||
project.status === 'processing' ||
($tag.rejectedStatuses.includes(project.status) && project.status !== 'withheld')
(tags.rejectedStatuses.includes(project.status) && project.status !== 'withheld')
"
class="iconified-button danger-button"
@click="openModerationModal('withheld')"
@@ -384,9 +386,9 @@
</button>
<button
v-if="
$tag.approvedStatuses.includes(project.status) ||
tags.approvedStatuses.includes(project.status) ||
project.status === 'processing' ||
($tag.rejectedStatuses.includes(project.status) && project.status !== 'rejected')
(tags.rejectedStatuses.includes(project.status) && project.status !== 'rejected')
"
class="iconified-button danger-button"
@click="openModerationModal('rejected')"
@@ -422,6 +424,8 @@
:toggle-collapsed="() => (collapsedChecklist = !collapsedChecklist)"
:all-members="allMembers"
:update-members="updateMembers"
:auth="auth"
:tags="tags"
/>
<div v-else-if="project.status === 'withheld'" class="card warning" aria-label="Warning">
{{ project.title }} has been removed from search by Modrinth's moderators. Please use
@@ -447,7 +451,7 @@
Prism Launcher</a
>.
</div>
<Promotion v-if="$tag.approvedStatuses.includes(project.status)" />
<Promotion v-if="tags.approvedStatuses.includes(project.status)" />
<div class="navigation-card">
<NavRow
:links="[
@@ -485,7 +489,7 @@
},
]"
/>
<div v-if="$auth.user && currentMember" class="input-group">
<div v-if="auth.user && currentMember" class="input-group">
<nuxt-link
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}/settings`"
class="iconified-button"
@@ -808,12 +812,15 @@ const data = useNuxtApp()
const route = useRoute()
const config = useRuntimeConfig()
const auth = await useAuth()
const user = await useUser()
const cosmetics = useCosmetics()
const tags = useTags()
if (
!route.params.id ||
!(
data.$tag.projectTypes.find((x) => x.id === route.params.type) ||
tags.value.projectTypes.find((x) => x.id === route.params.type) ||
route.params.type === 'project'
)
) {
@@ -833,28 +840,27 @@ try {
{ data: featuredVersions },
{ data: versions },
] = await Promise.all([
useAsyncData(
`project/${route.params.id}`,
() => useBaseFetch(`project/${route.params.id}`, data.$defaultHeaders()),
{
transform: (project) => {
if (project) {
project.actualProjectType = JSON.parse(JSON.stringify(project.project_type))
useAsyncData(`project/${route.params.id}`, () => useBaseFetch(`project/${route.params.id}`), {
transform: (project) => {
if (project) {
project.actualProjectType = JSON.parse(JSON.stringify(project.project_type))
project.project_type = data.$getProjectTypeForUrl(
project.project_type,
project.loaders,
tags.value
)
project.project_type = data.$getProjectTypeForUrl(project.project_type, project.loaders)
if (process.client && history.state && history.state.overrideProjectType) {
project.project_type = history.state.overrideProjectType
}
if (process.client && history.state && history.state.overrideProjectType) {
project.project_type = history.state.overrideProjectType
}
}
return project
},
}
),
return project
},
}),
useAsyncData(
`project/${route.params.id}/members`,
() => useBaseFetch(`project/${route.params.id}/members`, data.$defaultHeaders()),
() => useBaseFetch(`project/${route.params.id}/members`),
{
transform: (members) => {
members.forEach((it, index) => {
@@ -867,13 +873,13 @@ try {
}
),
useAsyncData(`project/${route.params.id}/dependencies`, () =>
useBaseFetch(`project/${route.params.id}/dependencies`, data.$defaultHeaders())
useBaseFetch(`project/${route.params.id}/dependencies`)
),
useAsyncData(`project/${route.params.id}/version?featured=true`, () =>
useBaseFetch(`project/${route.params.id}/version?featured=true`, data.$defaultHeaders())
useBaseFetch(`project/${route.params.id}/version?featured=true`)
),
useAsyncData(`project/${route.params.id}/version`, () =>
useBaseFetch(`project/${route.params.id}/version`, data.$defaultHeaders())
useBaseFetch(`project/${route.params.id}/version`)
),
])
@@ -910,23 +916,23 @@ if (project.value.project_type !== route.params.type || route.params.id !== proj
const members = ref(allMembers.value.filter((x) => x.accepted))
const currentMember = ref(
data.$auth.user ? allMembers.value.find((x) => x.user.id === data.$auth.user.id) : null
auth.value.user ? allMembers.value.find((x) => x.user.id === auth.value.user.id) : null
)
if (
!currentMember.value &&
data.$auth.user &&
data.$tag.staffRoles.includes(data.$auth.user.role)
auth.value.user &&
tags.value.staffRoles.includes(auth.value.user.role)
) {
currentMember.value = {
team_id: project.team_id,
user: data.$auth.user,
role: data.$auth.role,
permissions: data.$auth.user.role === 'admin' ? 1023 : 12,
user: auth.value.user,
role: auth.value.role,
permissions: auth.value.user.role === 'admin' ? 1023 : 12,
accepted: true,
payouts_split: 0,
avatar_url: data.$auth.user.avatar_url,
name: data.$auth.user.username,
avatar_url: auth.value.user.avatar_url,
name: auth.value.user.username,
}
}
@@ -943,7 +949,7 @@ featuredVersions.value = versions.value.filter((version) => featuredIds.includes
featuredVersions.value.sort((a, b) => {
const aLatest = a.game_versions[a.game_versions.length - 1]
const bLatest = b.game_versions[b.game_versions.length - 1]
const gameVersions = data.$tag.gameVersions.map((e) => e.version)
const gameVersions = tags.value.gameVersions.map((e) => e.version)
return gameVersions.indexOf(aLatest) - gameVersions.indexOf(bLatest)
})
@@ -967,7 +973,7 @@ const featuredGalleryImage = computed(() => project.value.gallery.find((img) =>
const requestedStatus = computed(() => project.value.requested_status ?? 'approved')
async function resetProject() {
const newProject = await useBaseFetch(`project/${project.value.id}`, data.$defaultHeaders())
const newProject = await useBaseFetch(`project/${project.value.id}`)
newProject.actualProjectType = JSON.parse(JSON.stringify(newProject.project_type))
@@ -986,7 +992,6 @@ async function clearMessage() {
moderation_message: null,
moderation_message_body: null,
},
...data.$defaultHeaders(),
})
project.value.moderator_message = null
@@ -1011,7 +1016,6 @@ async function setProcessing() {
body: {
status: 'processing',
},
...data.$defaultHeaders(),
})
project.value.status = 'processing'
@@ -1048,7 +1052,6 @@ async function patchProject(resData, quiet = false) {
await useBaseFetch(`project/${project.value.id}`, {
method: 'PATCH',
body: resData,
...data.$defaultHeaders(),
})
for (const key in resData) {
@@ -1099,7 +1102,6 @@ async function patchIcon(icon) {
{
method: 'PATCH',
body: icon,
...data.$defaultHeaders(),
}
)
await resetProject()
@@ -1136,7 +1138,7 @@ function openModerationModal(status) {
async function updateMembers() {
allMembers.value = await useAsyncData(
`project/${route.params.id}/members`,
() => useBaseFetch(`project/${route.params.id}/members`, data.$defaultHeaders()),
() => useBaseFetch(`project/${route.params.id}/members`),
{
transform: (members) => {
members.forEach((it, index) => {

View File

@@ -8,7 +8,7 @@
<Meta name="og:description" :contcent="metaDescription" />
</Head>
<Modal
v-if="$auth.user && currentMember"
v-if="currentMember"
ref="modal_edit_item"
:header="editIndex === -1 ? 'Upload gallery image' : 'Edit gallery item'"
>
@@ -127,7 +127,7 @@
</div>
</Modal>
<ModalConfirm
v-if="$auth.user && currentMember"
v-if="currentMember"
ref="modal_confirm"
title="Are you sure you want to delete this gallery image?"
description="This will remove this gallery image forever (like really forever)."
@@ -446,7 +446,6 @@ export default defineNuxtComponent({
await useBaseFetch(url, {
method: 'POST',
body: this.editFile,
...this.$defaultHeaders(),
})
await this.updateProject()
@@ -484,7 +483,6 @@ export default defineNuxtComponent({
await useBaseFetch(url, {
method: 'PATCH',
...this.$defaultHeaders(),
})
await this.updateProject()
@@ -511,7 +509,6 @@ export default defineNuxtComponent({
)}`,
{
method: 'DELETE',
...this.$defaultHeaders(),
}
)
@@ -528,7 +525,7 @@ export default defineNuxtComponent({
stopLoading()
},
async updateProject() {
const project = await useBaseFetch(`project/${this.project.id}`, this.$defaultHeaders())
const project = await useBaseFetch(`project/${this.project.id}`)
project.actualProjectType = JSON.parse(JSON.stringify(project.project_type))

View File

@@ -68,6 +68,7 @@
:project="project"
:set-status="setStatus"
:current-member="currentMember"
:auth="auth"
/>
</section>
</div>
@@ -104,9 +105,10 @@ const props = defineProps({
const emit = defineEmits(['update:project'])
const app = useNuxtApp()
const auth = await useAuth()
const { data: thread } = await useAsyncData(`thread/${props.project.thread_id}`, () =>
useBaseFetch(`thread/${props.project.thread_id}`, app.$defaultHeaders())
useBaseFetch(`thread/${props.project.thread_id}`)
)
async function setStatus(status) {
startLoading()
@@ -117,12 +119,11 @@ async function setStatus(status) {
await useBaseFetch(`project/${props.project.id}`, {
method: 'PATCH',
body: data,
...app.$defaultHeaders(),
})
const project = props.project
project.status = status
emit('update:project', project)
thread.value = await useBaseFetch(`thread/${thread.value.id}`, app.$defaultHeaders())
thread.value = await useBaseFetch(`thread/${thread.value.id}`)
} catch (err) {
app.$notify({
group: 'main',

View File

@@ -191,7 +191,7 @@
id="project-visibility"
v-model="visibility"
placeholder="Select one"
:options="$tag.approvedStatuses"
:options="tags.approvedStatuses"
:custom-label="(value) => $formatProjectStatus(value)"
:searchable="false"
:close-on-select="true"
@@ -315,6 +315,11 @@ export default defineNuxtComponent({
},
},
},
setup() {
const tags = useTags()
return { tags }
},
data() {
return {
name: this.project.title,
@@ -325,7 +330,7 @@ export default defineNuxtComponent({
clientSide: this.project.client_side,
serverSide: this.project.server_side,
deletedIcon: false,
visibility: this.$tag.approvedStatuses.includes(this.project.status)
visibility: this.tags.approvedStatuses.includes(this.project.status)
? this.project.status
: this.project.requested_status,
}
@@ -360,7 +365,7 @@ export default defineNuxtComponent({
if (this.serverSide !== this.project.server_side) {
data.server_side = this.serverSide
}
if (this.$tag.approvedStatuses.includes(this.project.status)) {
if (this.tags.approvedStatuses.includes(this.project.status)) {
if (this.visibility !== this.project.status) {
data.status = this.visibility
}
@@ -376,7 +381,7 @@ export default defineNuxtComponent({
},
methods: {
hasModifiedVisibility() {
const originalVisibility = this.$tag.approvedStatuses.includes(this.project.status)
const originalVisibility = this.tags.approvedStatuses.includes(this.project.status)
? this.project.status
: this.project.requested_status
@@ -407,7 +412,6 @@ export default defineNuxtComponent({
async deleteProject() {
await useBaseFetch(`project/${this.project.id}`, {
method: 'DELETE',
...this.$defaultHeaders(),
})
await initUserProjects()
await this.$router.push('/dashboard/review')
@@ -426,7 +430,6 @@ export default defineNuxtComponent({
async deleteIcon() {
await useBaseFetch(`project/${this.project.id}/icon`, {
method: 'DELETE',
...this.$defaultHeaders(),
})
await this.updateIcon()
this.$notify({

View File

@@ -96,7 +96,7 @@
<Multiselect
v-model="donationLink.platform"
placeholder="Select platform"
:options="$tag.donationPlatforms.map((x) => x.name)"
:options="tags.donationPlatforms.map((x) => x.name)"
:searchable="false"
:close-on-select="true"
:show-labels="false"
@@ -155,6 +155,11 @@ export default defineNuxtComponent({
},
},
},
setup() {
const tags = useTags()
return { tags }
},
data() {
const donationLinks = JSON.parse(JSON.stringify(this.project.donation_urls))
donationLinks.push({
@@ -195,7 +200,7 @@ export default defineNuxtComponent({
const donationLinks = this.donationLinks.filter((link) => link.url && link.platform)
donationLinks.forEach((link) => {
link.id = this.$tag.donationPlatforms.find(
link.id = this.tags.donationPlatforms.find(
(platform) => platform.name === link.platform
).short
})

View File

@@ -323,7 +323,6 @@ export default defineNuxtComponent({
await useBaseFetch(`team/${this.project.team}/members`, {
method: 'POST',
body: data,
...this.$defaultHeaders(),
})
this.currentUsername = ''
await this.updateMembers()
@@ -346,7 +345,6 @@ export default defineNuxtComponent({
`team/${this.project.team}/members/${this.allTeamMembers[index].user.id}`,
{
method: 'DELETE',
...this.$defaultHeaders(),
}
)
await this.updateMembers()
@@ -381,7 +379,6 @@ export default defineNuxtComponent({
{
method: 'PATCH',
body: data,
...this.$defaultHeaders(),
}
)
await this.updateMembers()
@@ -411,7 +408,6 @@ export default defineNuxtComponent({
body: {
user_id: this.allTeamMembers[index].user.id,
},
...this.$defaultHeaders(),
})
await this.updateMembers()
} catch (err) {
@@ -426,9 +422,7 @@ export default defineNuxtComponent({
stopLoading()
},
async updateMembers() {
this.allTeamMembers = (
await useBaseFetch(`team/${this.project.team}/members`, this.$defaultHeaders())
).map((it) => ({
this.allTeamMembers = (await useBaseFetch(`team/${this.project.team}/members`)).map((it) => ({
avatar_url: it.user.avatar_url,
name: it.user.username,
oldRole: it.role,

View File

@@ -152,13 +152,13 @@ export default defineNuxtComponent({
},
data() {
return {
selectedTags: this.$sortedCategories.filter(
selectedTags: this.$sortedCategories().filter(
(x) =>
x.project_type === this.project.actualProjectType &&
(this.project.categories.includes(x.name) ||
this.project.additional_categories.includes(x.name))
),
featuredTags: this.$sortedCategories.filter(
featuredTags: this.$sortedCategories().filter(
(x) =>
x.project_type === this.project.actualProjectType &&
this.project.categories.includes(x.name)
@@ -168,7 +168,7 @@ export default defineNuxtComponent({
computed: {
categoryLists() {
const lists = {}
this.$sortedCategories.forEach((x) => {
this.$sortedCategories().forEach((x) => {
if (x.project_type === this.project.actualProjectType) {
const header = x.header
if (!lists[header]) {

View File

@@ -8,7 +8,7 @@
<Meta name="og:description" :content="metaDescription" />
</Head>
<ModalConfirm
v-if="$auth.user && currentMember"
v-if="currentMember"
ref="modal_confirm"
title="Are you sure you want to delete this version?"
description="This will remove this version forever (like really forever)."
@@ -17,12 +17,12 @@
@proceed="deleteVersion()"
/>
<ModalReport
v-if="$auth.user"
v-if="auth.user"
ref="modal_version_report"
:item-id="version.id"
item-type="version"
/>
<Modal v-if="$auth.user && currentMember" ref="modal_package_mod" header="Package data pack">
<Modal v-if="auth.user && currentMember" ref="modal_package_mod" header="Package data pack">
<div class="modal-package-mod universal-labels">
<div class="markdown-body">
<p>
@@ -116,7 +116,7 @@
Create
</button>
<nuxt-link
v-if="$auth.user"
v-if="auth.user"
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}/versions`"
class="iconified-button"
>
@@ -164,7 +164,7 @@
<ReportIcon aria-hidden="true" />
Report
</button>
<a v-if="!$auth.user" class="iconified-button" :href="getAuthUrl()" rel="noopener nofollow">
<a v-if="!auth.user" class="iconified-button" :href="getAuthUrl()" rel="noopener nofollow">
<ReportIcon aria-hidden="true" />
Report
</a>
@@ -181,7 +181,7 @@
<button
v-if="
currentMember &&
version.loaders.some((x) => $tag.loaderData.dataPackLoaders.includes(x))
version.loaders.some((x) => tags.loaderData.dataPackLoaders.includes(x))
"
class="iconified-button"
@click="$refs.modal_package_mod.show()"
@@ -390,7 +390,7 @@
</span>
<multiselect
v-if="
version.loaders.some((x) => $tag.loaderData.dataPackLoaders.includes(x)) &&
version.loaders.some((x) => tags.loaderData.dataPackLoaders.includes(x)) &&
isEditing &&
primaryFile.hashes.sha1 !== file.hashes.sha1
"
@@ -457,7 +457,7 @@
<span class="file-size">({{ $formatBytes(file.size) }})</span>
</span>
<multiselect
v-if="version.loaders.some((x) => $tag.loaderData.dataPackLoaders.includes(x))"
v-if="version.loaders.some((x) => tags.loaderData.dataPackLoaders.includes(x))"
v-model="newFileTypes[index]"
class="raised-multiselect"
placeholder="Select file type"
@@ -484,7 +484,7 @@
</div>
<div class="additional-files">
<h4>Upload additional files</h4>
<span v-if="version.loaders.some((x) => $tag.loaderData.dataPackLoaders.includes(x))">
<span v-if="version.loaders.some((x) => tags.loaderData.dataPackLoaders.includes(x))">
Used for additional files such as required/optional resource packs
</span>
<span v-else>Used for files such as sources or Javadocs.</span>
@@ -566,14 +566,14 @@
v-if="isEditing"
v-model="version.loaders"
:options="
$tag.loaders
tags.loaders
.filter((x) =>
x.supported_project_types.includes(project.actualProjectType.toLowerCase())
)
.map((it) => it.name)
"
:custom-label="(value) => $formatCategory(value)"
:loading="$tag.loaders.length === 0"
:loading="tags.loaders.length === 0"
:multiple="true"
:searchable="false"
:show-no-results="false"
@@ -593,12 +593,12 @@
v-model="version.game_versions"
:options="
showSnapshots
? $tag.gameVersions.map((x) => x.version)
: $tag.gameVersions
? tags.gameVersions.map((x) => x.version)
: tags.gameVersions
.filter((it) => it.version_type === 'release')
.map((x) => x.version)
"
:loading="$tag.gameVersions.length === 0"
:loading="tags.gameVersions.length === 0"
:multiple="true"
:searchable="true"
:show-no-results="false"
@@ -772,6 +772,9 @@ export default defineNuxtComponent({
const data = useNuxtApp()
const route = useRoute()
const auth = await useAuth()
const tags = useTags()
const path = route.name.split('-')
const mode = path[path.length - 1]
@@ -828,7 +831,7 @@ export default defineNuxtComponent({
const inferredData = await inferVersionInfo(
replaceFile,
props.project,
data.$tag.gameVersions
tags.value.gameVersions
)
version = {
@@ -895,6 +898,8 @@ export default defineNuxtComponent({
const order = ['required', 'optional', 'incompatible', 'embedded']
return {
auth,
tags,
fileTypes: ref(fileTypes),
oldFileTypes: ref(oldFileTypes),
isCreating: ref(isCreating),
@@ -1110,7 +1115,6 @@ export default defineNuxtComponent({
body: formData,
headers: {
'Content-Disposition': formData,
Authorization: this.$auth.token,
},
})
}
@@ -1135,13 +1139,11 @@ export default defineNuxtComponent({
}
}),
},
...this.$defaultHeaders(),
})
for (const hash of this.deleteFiles) {
await useBaseFetch(`version_file/${hash}?version_id=${this.version.id}`, {
method: 'DELETE',
...this.$defaultHeaders(),
})
}
@@ -1246,7 +1248,6 @@ export default defineNuxtComponent({
body: formData,
headers: {
'Content-Disposition': formData,
Authorization: this.$auth.token,
},
})
@@ -1263,7 +1264,6 @@ export default defineNuxtComponent({
await useBaseFetch(`version/${this.version.id}`, {
method: 'DELETE',
...this.$defaultHeaders(),
})
await this.resetProjectVersions()
@@ -1279,7 +1279,7 @@ export default defineNuxtComponent({
this.version,
this.primaryFile,
this.members,
this.$tag.gameVersions,
this.tags.gameVersions,
this.packageLoaders
)
@@ -1324,12 +1324,9 @@ export default defineNuxtComponent({
},
async resetProjectVersions() {
const [versions, featuredVersions, dependencies] = await Promise.all([
useBaseFetch(`project/${this.version.project_id}/version`, this.$defaultHeaders()),
useBaseFetch(
`project/${this.version.project_id}/version?featured=true`,
this.$defaultHeaders()
),
useBaseFetch(`project/${this.version.project_id}/dependencies`, this.$defaultHeaders()),
useBaseFetch(`project/${this.version.project_id}/version`),
useBaseFetch(`project/${this.version.project_id}/version?featured=true`),
useBaseFetch(`project/${this.version.project_id}/dependencies`),
])
const newCreatedVersions = this.$computeVersions(versions, this.members)