Skip to content
Closed
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
v-if="showUpdatePill"
type="outlined"
native-type="button"
class="!h-[34px] text-sm !transition-[opacity,transform,background-color,color,filter] !duration-200 ease-out !text-brand [&>svg]:!text-brand !shadow-[inset_0_0_0_1px_var(--color-brand)] hover:!bg-brand focus-visible:!bg-brand hover:!text-[var(--color-accent-contrast)] focus-visible:!text-[var(--color-accent-contrast)]"
class="!h-[34px] text-sm !transition-[opacity,transform,background-color,color,filter] !duration-200 ease-out !text-brand [&>svg]:!text-inherit !shadow-[inset_0_0_0_1px_var(--color-brand)] hover:!bg-brand focus-visible:!bg-brand hover:!text-[var(--color-accent-contrast)] focus-visible:!text-[var(--color-accent-contrast)]"
:class="{
'opacity-0 scale-[0.96]': finishedDownloading && !animateReadyPill,
'opacity-100 scale-100': finishedDownloading && animateReadyPill,
Expand Down
21 changes: 16 additions & 5 deletions apps/app-frontend/src/composables/browse/use-app-server-browse.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Labrinth } from '@modrinth/api-client'
import { CheckIcon, PlayIcon, PlusIcon, StopCircleIcon } from '@modrinth/assets'
import { CheckIcon, PlayIcon, PlusIcon, SpinnerIcon, StopCircleIcon } from '@modrinth/assets'
import type { CardAction } from '@modrinth/ui'
import { commonMessages, defineMessages, useDebugLogger, useVIntl } from '@modrinth/ui'
import { useQueryClient } from '@tanstack/vue-query'
Expand Down Expand Up @@ -75,6 +75,7 @@ export function useAppServerBrowse(options: UseAppServerBrowseOptions) {
const debugLog = useDebugLogger('BrowseServer')
const serverPings = shallowRef<Record<string, number | undefined>>({})
const runningServerProjects = ref<Record<string, string>>({})
const preparingServerProjects = ref<string[]>([])
const lastServerHits = shallowRef<Labrinth.Search.v3.ResultSearchProject[]>([])
const contextMenuRef = ref<ContextMenuHandle | null>(null)
let serverPingsActive = true
Expand Down Expand Up @@ -109,9 +110,16 @@ export function useAppServerBrowse(options: UseAppServerBrowseOptions) {
}

async function handlePlayServerProject(projectId: string) {
if (preparingServerProjects.value.includes(projectId)) return

debugLog('handlePlayServerProject', projectId)
await options.playServerProject(projectId)
checkServerRunningStates(lastServerHits.value)
preparingServerProjects.value.push(projectId)
try {
await options.playServerProject(projectId)
checkServerRunningStates(lastServerHits.value)
} finally {
preparingServerProjects.value = preparingServerProjects.value.filter((id) => id !== projectId)
}
}

async function handleAddServerToInstance(project: Labrinth.Search.v3.ResultSearchProject) {
Expand Down Expand Up @@ -247,13 +255,16 @@ export function useAppServerBrowse(options: UseAppServerBrowseOptions) {
})
} else {
const isInstalling = options.installingServerProjects.value.includes(serverResult.project_id)
const isPreparing = preparingServerProjects.value.includes(serverResult.project_id)
const isBusy = isInstalling || isPreparing
actions.push({
key: 'play',
label: formatMessage(
isInstalling ? commonMessages.installingLabel : commonMessages.playButton,
),
icon: PlayIcon,
disabled: isInstalling,
icon: isBusy ? SpinnerIcon : PlayIcon,
iconClass: isBusy ? 'animate-spin' : undefined,
disabled: isBusy,
color: 'brand',
type: 'outlined',
onClick: () => handlePlayServerProject(serverResult.project_id),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
:ping="ping"
:minecraft-server="minecraftServer"
:show-instance-play-time="showInstancePlayTime"
:playtime-label="playtimeLabel"
:playtime-label="playtimeLabel ?? formatMessage(messages.neverPlayed)"
/>
<PageHeaderMetadata v-else>
<PageHeaderMetadataItem
Expand Down
122 changes: 62 additions & 60 deletions apps/app-frontend/src/pages/project/Gallery.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,70 +14,72 @@
</span>
</Card>
</div>
<div v-if="expandedGalleryItem" class="expanded-image-modal" @click="hideImage">
<div class="content">
<img
class="image"
:class="{ 'zoomed-in': zoomedIn }"
:src="
expandedGalleryItem.raw_url
? expandedGalleryItem.raw_url
: 'https://cdn.modrinth.com/placeholder-banner.svg'
"
:alt="expandedGalleryItem.title ? expandedGalleryItem.title : 'gallery-image'"
@click.stop="() => {}"
/>

<div class="floating" @click.stop="() => {}">
<div class="text">
<h2 v-if="expandedGalleryItem.title">
{{ expandedGalleryItem.title }}
</h2>
<p v-if="expandedGalleryItem.description">
{{ expandedGalleryItem.description }}
</p>
</div>
<div class="controls">
<div class="buttons">
<IconButton label="Close" class="close" @click="hideImage">
<XIcon aria-hidden="true" />
</IconButton>
<ButtonLink
class="open btn icon-only !w-9 !px-0 !rounded-full"
target="_blank"
:href="
expandedGalleryItem.raw_url
? expandedGalleryItem.raw_url
: 'https://cdn.modrinth.com/placeholder-banner.svg'
"
>
<ExternalIcon aria-hidden="true" />
</ButtonLink>
<IconButton label="Toggle zoom" @click="zoomedIn = !zoomedIn">
<ExpandIcon v-if="!zoomedIn" aria-hidden="true" />
<ContractIcon v-else aria-hidden="true" />
</IconButton>
<IconButton
v-if="filteredGallery.length > 1"
label="Previous image"
class="previous"
@click="previousImage()"
>
<LeftArrowIcon aria-hidden="true" />
</IconButton>
<IconButton
v-if="filteredGallery.length > 1"
label="Next image"
class="next"
@click="nextImage()"
>
<RightArrowIcon aria-hidden="true" />
</IconButton>
<Teleport to="#teleports">
<div v-if="expandedGalleryItem" class="expanded-image-modal" @click="hideImage">
<div class="content">
<img
class="image"
:class="{ 'zoomed-in': zoomedIn }"
:src="
expandedGalleryItem.raw_url
? expandedGalleryItem.raw_url
: 'https://cdn.modrinth.com/placeholder-banner.svg'
"
:alt="expandedGalleryItem.title ? expandedGalleryItem.title : 'gallery-image'"
@click.stop="() => {}"
/>

<div class="floating" @click.stop="() => {}">
<div class="text">
<h2 v-if="expandedGalleryItem.title">
{{ expandedGalleryItem.title }}
</h2>
<p v-if="expandedGalleryItem.description">
{{ expandedGalleryItem.description }}
</p>
</div>
<div class="controls">
<div class="buttons">
<IconButton label="Close" class="close" @click="hideImage">
<XIcon aria-hidden="true" />
</IconButton>
<ButtonLink
class="open btn icon-only !w-9 !px-0 !rounded-full"
target="_blank"
:href="
expandedGalleryItem.raw_url
? expandedGalleryItem.raw_url
: 'https://cdn.modrinth.com/placeholder-banner.svg'
"
>
<ExternalIcon aria-hidden="true" />
</ButtonLink>
<IconButton label="Toggle zoom" @click="zoomedIn = !zoomedIn">
<ExpandIcon v-if="!zoomedIn" aria-hidden="true" />
<ContractIcon v-else aria-hidden="true" />
</IconButton>
<IconButton
v-if="filteredGallery.length > 1"
label="Previous image"
class="previous"
@click="previousImage()"
>
<LeftArrowIcon aria-hidden="true" />
</IconButton>
<IconButton
v-if="filteredGallery.length > 1"
label="Next image"
class="next"
@click="nextImage()"
>
<RightArrowIcon aria-hidden="true" />
</IconButton>
</div>
</div>
</div>
</div>
</div>
</div>
</Teleport>
</template>

<script setup>
Expand Down
8 changes: 4 additions & 4 deletions apps/app-frontend/src/providers/server-install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export interface ServerInstallContext {
project: Labrinth.Projects.v3.Project,
modpackVersionId: string | null,
callback?: () => void,
) => void
) => Promise<void>
>,
) => void
setUpdateToPlayModal: (
Expand Down Expand Up @@ -84,7 +84,7 @@ export function createServerInstall(opts: {
project: Labrinth.Projects.v3.Project,
modpackVersionId: string | null,
callback?: () => void,
) => void
) => Promise<void>
> | null = null
let updateToPlayModalRef: ModalRef<
(instance: GameInstance, activeVersionId: string | null, callback?: () => void) => void
Expand Down Expand Up @@ -246,10 +246,10 @@ export function createServerInstall(opts: {
project_id: contentProjectId,
version_id: contentVersionId,
title: project.title,
icon_url: project.icon_url,
},
{
name: project.title,
iconPath: project.icon_url ?? null,
link: {
type: 'server_project_modpack',
server_project_id: serverProjectId,
Expand Down Expand Up @@ -314,7 +314,7 @@ export function createServerInstall(opts: {
return
}
if (isModpack && !instance) {
installToPlayModalRef?.show(projectV3, modpackVersionId, async () => {
await installToPlayModalRef?.show(projectV3, modpackVersionId, async () => {
const newInstance = await findInstalledInstance(project.id)
if (!newInstance) return
showModpackInstallSuccess(newInstance, serverAddress)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,9 +330,13 @@ const sharedInstanceBanPending = ref(false)
const sharedInstanceVersions = new Map<string, SharedInstances.Instances.v1.InstanceVersion>()
let sharedInstanceDetailsRequest: Promise<void> | null = null

watch(isThreadCollapsed, (collapsed) => {
if (!collapsed) void loadSharedInstanceDetails()
})
watch(
isThreadCollapsed,
(collapsed) => {
if (!collapsed) void loadSharedInstanceDetails()
},
{ immediate: true },
)

const didCloseReport = ref(false)
const reportClosed = computed(() => {
Expand Down
83 changes: 42 additions & 41 deletions packages/app-lib/src/api/pack/install_from.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,6 @@ pub(crate) async fn generate_pack_from_version_id_with_reporter(
reporter: InstallProgressReporter,
) -> crate::Result<CreatePack> {
let state = State::get().await?;
let has_icon_url = icon_url.is_some();

let version = CachedEntry::get_version(
&version_id,
Expand Down Expand Up @@ -443,48 +442,28 @@ pub(crate) async fn generate_pack_from_version_id_with_reporter(
.update(InstallPhaseId::ResolvingPack, None, details.clone())
.await?;

let project = CachedEntry::get_project(
&version.project_id,
None,
&state.pool,
&state.api_semaphore,
)
.await?
.ok_or_else(|| {
crate::ErrorKind::InputError(
"Invalid project ID specified!".to_string(),
)
})?;

// Only fetch the pack icon when icon_url is provided (new profile).
// When installing to an existing profile (e.g. server projects),
// icon_url is None and we preserve the profile's existing icon.
let icon = if has_icon_url {
if let Some(icon_url) = project.icon_url {
let state = State::get().await?;
reporter
.set_context(
InstallErrorContext::new("download modpack icon")
.urls(vec![icon_url.clone()])
.project_id(project_id.clone())
.version_id(version_id.clone())
.build(),
)
.await?;
let icon_bytes = fetch(
&icon_url,
None,
None,
None,
&state.fetch_semaphore,
&state.pool,
// When no icon URL is supplied, preserve the instance's existing icon.
let icon = if let Some(icon_url) = icon_url {
reporter
.set_context(
InstallErrorContext::new("download modpack icon")
.urls(vec![icon_url.clone()])
.project_id(project_id.clone())
.version_id(version_id.clone())
.build(),
)
.await?;
let icon_bytes = fetch(
&icon_url,
None,
None,
None,
&state.fetch_semaphore,
&state.pool,
)
.await?;

Some(crate::api::instance::cache_icon(icon_bytes, &state).await?)
} else {
None
}
Some(crate::api::instance::cache_icon(icon_bytes, &state).await?)
} else {
None
};
Expand Down Expand Up @@ -587,7 +566,7 @@ pub async fn set_instance_information(
} else {
None
};
let link = match (&description.project_id, &description.version_id) {
let pack_link = match (&description.project_id, &description.version_id) {
(Some(project_id), Some(version_id)) => {
Some(InstanceLink::ModrinthModpack {
project_id: project_id.clone(),
Expand All @@ -605,13 +584,35 @@ pub async fn set_instance_information(
}
_ => None,
};
let existing_link = crate::api::instance::get(&instance_id)
.await?
.map(|metadata| metadata.link);
let link = match existing_link {
Some(
link @ (InstanceLink::ServerProject { .. }
| InstanceLink::ServerProjectModpack { .. }
| InstanceLink::ModrinthHosting { .. }
| InstanceLink::SharedInstance { .. }),
) => Some(link),
_ => pack_link,
};
let source_kind = match &link {
Some(InstanceLink::ModrinthModpack { .. }) => {
Some(ContentSourceKind::ModrinthModpack)
}
Some(
InstanceLink::ServerProject { .. }
| InstanceLink::ServerProjectModpack { .. },
) => Some(ContentSourceKind::ServerProject),
Some(InstanceLink::ModrinthHosting { .. }) => {
Some(ContentSourceKind::ModrinthHosting)
}
Some(InstanceLink::ImportedModpack { .. }) => {
Some(ContentSourceKind::ImportedModpack)
}
Some(InstanceLink::SharedInstance { .. }) => {
Some(ContentSourceKind::SharedInstance)
}
_ => None,
};
crate::api::instance::edit(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -929,10 +929,7 @@ async fn content_files_to_content_items(
&meta.versions_v3,
),
owner,
has_update: file.update_version_id.is_some()
&& !file.source_kind.is_some_and(
ContentSourceKind::is_shared_instance_managed,
),
has_update: file.update_version_id.is_some(),
update_version_id: file.update_version_id.clone(),
date_added: modification_times[index].clone(),
source_kind: file.source_kind,
Expand Down
Loading
Loading