fix: 404 when returning to collections dashboard (#5963)

* fix 404 when returning to collections dashboard, fix a couple hydration
issues

* fix clippy

* fmt

* fix hydration issue on revenue page

* fix transfer history page error

---------

Co-authored-by: aecsocket <aecsocket@tutanota.com>
This commit is contained in:
Prospector
2026-05-02 09:33:19 -07:00
committed by GitHub
parent c2359275ff
commit be618d96f4
10 changed files with 94 additions and 79 deletions

View File

@@ -1,7 +1,9 @@
<template> <template>
<NuxtLayout> <NuxtLayout>
<NuxtRouteAnnouncer /> <NuxtRouteAnnouncer />
<LoadingBar /> <ClientOnly>
<LoadingBar />
</ClientOnly>
<NotificationPanel /> <NotificationPanel />
<I18nDebugPanel /> <I18nDebugPanel />
<NuxtPage /> <NuxtPage />

View File

@@ -186,7 +186,9 @@
getProjectTypeSentenceMessage( getProjectTypeSentenceMessage(
projectTypes.length === 1 ? projectTypes[0] : 'project', projectTypes.length === 1 ? projectTypes[0] : 'project',
), ),
{ count: formatCompactNumberPlural(projects?.length || 0) }, {
count: formatCompactNumberPlural(projects?.length || 0),
},
), ),
}" }"
> >
@@ -532,8 +534,8 @@ const returnLink = computed(() => {
return null return null
}) })
const isFollowingCollection = computed(() => route.params.id === 'following')
const collectionId = computed(() => route.params.id) const collectionId = computed(() => route.params.id)
const isFollowingCollection = computed(() => collectionId.value === 'following')
// Static collection for "following" page // Static collection for "following" page
const followingCollection = computed(() => const followingCollection = computed(() =>
@@ -560,13 +562,13 @@ const {
} = useQuery({ } = useQuery({
queryKey: computed(() => ['collection', collectionId.value]), queryKey: computed(() => ['collection', collectionId.value]),
queryFn: () => api.labrinth.collections.get(collectionId.value), queryFn: () => api.labrinth.collections.get(collectionId.value),
enabled: computed(() => !isFollowingCollection.value), enabled: computed(() => !!collectionId.value && !isFollowingCollection.value),
}) })
watch( watch(
collectionError, collectionError,
(error) => { (error) => {
if (error && !isFollowingCollection.value) { if (error && collectionId.value && !isFollowingCollection.value) {
const status = error.statusCode ?? error.status ?? 404 const status = error.statusCode ?? error.status ?? 404
showError({ showError({
fatal: true, fatal: true,

View File

@@ -70,7 +70,12 @@
date: date.date ? formatDate(date.date) : '', date: date.date ? formatDate(date.date) : '',
}) })
}} }}
<Tooltip theme="dismissable-prompt" :triggers="['hover', 'focus']" no-auto-focus> <Tooltip
theme="dismissable-prompt"
:triggers="['hover', 'focus']"
no-auto-focus
:aria-id="`${baseId}-date-segment-tooltip-${i}`"
>
<nuxt-link <nuxt-link
class="inline-flex items-center justify-center text-link" class="inline-flex items-center justify-center text-link"
to="/legal/cmp-info#pending" to="/legal/cmp-info#pending"
@@ -99,7 +104,12 @@
class="zone--striped-small zone--striped--gray my-auto block size-4 rounded-full bg-button-bg opacity-90 md:size-5" class="zone--striped-small zone--striped--gray my-auto block size-4 rounded-full bg-button-bg opacity-90 md:size-5"
></span> ></span>
{{ formatMessage(messages.processing) }} {{ formatMessage(messages.processing) }}
<Tooltip theme="dismissable-prompt" :triggers="['hover', 'focus']" no-auto-focus> <Tooltip
theme="dismissable-prompt"
:triggers="['hover', 'focus']"
no-auto-focus
:aria-id="`${baseId}-processing-tooltip`"
>
<InProgressIcon class="inline-block size-4 align-middle md:size-5" /> <InProgressIcon class="inline-block size-4 align-middle md:size-5" />
<template #popper> <template #popper>
<div class="w-[250px] font-semibold text-contrast"> <div class="w-[250px] font-semibold text-contrast">
@@ -292,6 +302,8 @@ type RevenueBarSegment = {
const hoveredSeg = ref<string | null>(null) const hoveredSeg = ref<string | null>(null)
const baseId = useId()
const withdrawModal = ref<InstanceType<typeof CreatorWithdrawModal>>() const withdrawModal = ref<InstanceType<typeof CreatorWithdrawModal>>()
async function openWithdrawModal() { async function openWithdrawModal() {
withdrawModal.value?.show?.() withdrawModal.value?.show?.()

View File

@@ -117,6 +117,53 @@ const formatMonth = useFormatDateTime({
const client = injectModrinthClient() const client = injectModrinthClient()
const generatedState = useGeneratedState() const generatedState = useGeneratedState()
const messages = defineMessages({
transactionsHeader: {
id: 'dashboard.revenue.transactions.header',
defaultMessage: 'Transactions',
},
headTitle: {
id: 'dashboard.revenue.transactions.head-title',
defaultMessage: 'Transaction history',
},
received: {
id: 'dashboard.revenue.stats.received',
defaultMessage: 'Received',
},
withdrawn: {
id: 'dashboard.revenue.stats.withdrawn',
defaultMessage: 'Withdrawn',
},
transactions: {
id: 'dashboard.revenue.stats.transactions',
defaultMessage: 'Transactions',
},
noTransactions: {
id: 'dashboard.revenue.transactions.none',
defaultMessage: 'No transactions',
},
noTransactionsDesc: {
id: 'dashboard.revenue.transactions.none.desc',
defaultMessage: 'Your payouts and withdrawals will appear here.',
},
downloadCsv: {
id: 'dashboard.revenue.transactions.btn.download-csv',
defaultMessage: 'Download as CSV',
},
allYears: {
id: 'dashboard.revenue.transactions.year.all',
defaultMessage: 'All years',
},
thisMonth: {
id: 'dashboard.revenue.transactions.period.this-month',
defaultMessage: 'This month',
},
lastMonth: {
id: 'dashboard.revenue.transactions.period.last-month',
defaultMessage: 'Last month',
},
})
useHead({ useHead({
title: () => `${formatMessage(messages.headTitle)} - Modrinth`, title: () => `${formatMessage(messages.headTitle)} - Modrinth`,
}) })
@@ -318,51 +365,4 @@ const downloadTransactionsCSV = () => {
} }
const onDownloadCSV = useClientTry(async () => await downloadTransactionsCSV()) const onDownloadCSV = useClientTry(async () => await downloadTransactionsCSV())
const messages = defineMessages({
transactionsHeader: {
id: 'dashboard.revenue.transactions.header',
defaultMessage: 'Transactions',
},
headTitle: {
id: 'dashboard.revenue.transactions.head-title',
defaultMessage: 'Transaction history',
},
received: {
id: 'dashboard.revenue.stats.received',
defaultMessage: 'Received',
},
withdrawn: {
id: 'dashboard.revenue.stats.withdrawn',
defaultMessage: 'Withdrawn',
},
transactions: {
id: 'dashboard.revenue.stats.transactions',
defaultMessage: 'Transactions',
},
noTransactions: {
id: 'dashboard.revenue.transactions.none',
defaultMessage: 'No transactions',
},
noTransactionsDesc: {
id: 'dashboard.revenue.transactions.none.desc',
defaultMessage: 'Your payouts and withdrawals will appear here.',
},
downloadCsv: {
id: 'dashboard.revenue.transactions.btn.download-csv',
defaultMessage: 'Download as CSV',
},
allYears: {
id: 'dashboard.revenue.transactions.year.all',
defaultMessage: 'All years',
},
thisMonth: {
id: 'dashboard.revenue.transactions.period.this-month',
defaultMessage: 'This month',
},
lastMonth: {
id: 'dashboard.revenue.transactions.period.last-month',
defaultMessage: 'Last month',
},
})
</script> </script>

View File

@@ -228,8 +228,7 @@ pub async fn install_zipped_mrpack_files(
let num_files = pack.files.len(); let num_files = pack.files.len();
loading_try_for_each_concurrent( loading_try_for_each_concurrent(
futures::stream::iter(pack.files.into_iter()) futures::stream::iter(pack.files).map(Ok::<PackFile, crate::Error>),
.map(Ok::<PackFile, crate::Error>),
None, None,
Some(&loading_bar), Some(&loading_bar),
70.0, 70.0,

View File

@@ -150,8 +150,8 @@ pub async fn resolve_server_address(
match resolver.srv_lookup(format!("_minecraft._tcp.{host}")).await { match resolver.srv_lookup(format!("_minecraft._tcp.{host}")).await {
Err(e) Err(e)
if e.proto() if e.proto()
.filter(|x| x.kind().is_no_records_found()) .as_ref()
.is_some() => .is_some_and(|x| x.kind().is_no_records_found()) =>
{ {
None None
} }

View File

@@ -791,7 +791,7 @@ pub async fn remove_server_from_profile(
index: usize, index: usize,
) -> Result<()> { ) -> Result<()> {
let mut servers = servers_data::read(profile_path).await?; let mut servers = servers_data::read(profile_path).await?;
if servers.get(index).filter(|x| !x.hidden).is_none() { if servers.get(index).as_ref().is_none_or(|x| x.hidden) {
return Err(ErrorKind::InputError(format!( return Err(ErrorKind::InputError(format!(
"No removable server at index {index}" "No removable server at index {index}"
)) ))

View File

@@ -59,31 +59,28 @@ pub async fn init_watcher() -> crate::Result<FileWatcher> {
.nth(1) .nth(1)
.map(|x| x.as_os_str()); .map(|x| x.as_os_str());
if first_file_name if first_file_name
.filter(|x| *x == "crash-reports") .as_ref()
.is_some() .is_some_and(|x| *x == "crash-reports")
&& e.path && e.path
.extension() .extension()
.filter(|x| *x == "txt") .as_ref()
.is_some() .is_some_and(|x| *x == "txt")
{ {
crash_task(profile_path_str); crash_task(profile_path_str);
} else if !visited_profiles.contains(&profile_path) } else if !visited_profiles.contains(&profile_path)
{ {
let event = if first_file_name let event = if first_file_name
.filter(|x| *x == "servers.dat") .as_ref()
.is_some() .is_some_and(|x| *x == "servers.dat")
{ {
Some(ProfilePayloadType::ServersUpdated) Some(ProfilePayloadType::ServersUpdated)
} else if first_file_name } else if first_file_name.as_ref().is_some_and(|x| {
.filter(|x| { *x == "saves"
*x == "saves" && e.path
&& e.path .file_name()
.file_name() .as_ref()
.filter(|x| *x == "level.dat") .is_some_and(|x| *x == "level.dat")
.is_some() }) {
})
.is_some()
{
tracing::info!( tracing::info!(
"World updated: {}", "World updated: {}",
e.path.display() e.path.display()
@@ -113,8 +110,8 @@ pub async fn init_watcher() -> crate::Result<FileWatcher> {
} }
Some(ProfilePayloadType::WorldUpdated { world }) Some(ProfilePayloadType::WorldUpdated { world })
} else if first_file_name } else if first_file_name
.filter(|x| *x == "saves") .as_ref()
.is_none() .is_none_or(|x| *x != "saves")
{ {
Some(ProfilePayloadType::Synced) Some(ProfilePayloadType::Synced)
} else { } else {

View File

@@ -283,7 +283,7 @@ fn check_modpack_update(
.collect(); .collect();
// Sort by date_published descending (newest first) // Sort by date_published descending (newest first)
compatible_versions.sort_by(|a, b| b.date_published.cmp(&a.date_published)); compatible_versions.sort_by_key(|b| std::cmp::Reverse(b.date_published));
// Find the newest compatible version // Find the newest compatible version
if let Some(newest) = compatible_versions.first() { if let Some(newest) = compatible_versions.first() {

View File

@@ -1,8 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { Menu } from 'floating-vue' import { Menu } from 'floating-vue'
import { useId } from 'vue'
import { TagItem, TagTagItem } from '../base' import { TagItem, TagTagItem } from '../base'
const id = useId()
defineProps<{ defineProps<{
tags: string[] tags: string[]
}>() }>()
@@ -13,7 +16,7 @@ defineOptions({
</script> </script>
<template> <template>
<Menu :delay="{ hide: 50, show: 0 }" no-auto-focus> <Menu :delay="{ hide: 50, show: 0 }" no-auto-focus :aria-id="id">
<TagItem v-if="tags.length > 0" v-bind="$attrs" tabindex="0"> +{{ tags.length }} </TagItem> <TagItem v-if="tags.length > 0" v-bind="$attrs" tabindex="0"> +{{ tags.length }} </TagItem>
<template #popper> <template #popper>
<div class="flex gap-1 flex-wrap max-w-[20rem]"> <div class="flex gap-1 flex-wrap max-w-[20rem]">