diff --git a/apps/app-frontend/src/components/ui/instance_settings/SharingSettings.vue b/apps/app-frontend/src/components/ui/instance_settings/SharingSettings.vue index 9e51615ba6..cd6fcb038e 100644 --- a/apps/app-frontend/src/components/ui/instance_settings/SharingSettings.vue +++ b/apps/app-frontend/src/components/ui/instance_settings/SharingSettings.vue @@ -72,7 +72,6 @@ import { ButtonStyled, CopyCode, defineMessages, - injectNotificationManager, Table, type TableColumn, useFormatDateTime, @@ -86,10 +85,11 @@ import ConfirmRevokeSharedInstanceInviteModal from '@/components/ui/shared-insta import SharedInstanceInstallationSettingsControls from '@/components/ui/shared-instances/SharedInstanceInstallationSettingsControls.vue' import { config } from '@/config' import { type SharedInstanceInvite, unpublish_shared_instance } from '@/helpers/instance' +import { useSharedInstanceErrors } from '@/helpers/shared-instance-errors' import { injectInstanceSettings } from '@/providers/instance-settings' const { instance, offline, onUnlinked } = injectInstanceSettings() -const { handleError } = injectNotificationManager() +const { notifySharedInstanceError } = useSharedInstanceErrors() const { formatMessage } = useVIntl() const queryClient = useQueryClient() const unpublishing = ref(false) @@ -162,7 +162,7 @@ async function unpublishSharedInstance() { await queryClient.invalidateQueries({ queryKey: ['linkedModpackInfo', instance.value.id] }) onUnlinked() } catch (error) { - handleError(error) + notifySharedInstanceError(error) } finally { unpublishing.value = false } diff --git a/apps/app-frontend/src/components/ui/shared-instances/shared-instance-invite-handler/use-shared-instance-invite-handler.ts b/apps/app-frontend/src/components/ui/shared-instances/shared-instance-invite-handler/use-shared-instance-invite-handler.ts index 982420a776..74e68e5ed2 100644 --- a/apps/app-frontend/src/components/ui/shared-instances/shared-instance-invite-handler/use-shared-instance-invite-handler.ts +++ b/apps/app-frontend/src/components/ui/shared-instances/shared-instance-invite-handler/use-shared-instance-invite-handler.ts @@ -19,6 +19,7 @@ import { install_shared_instance, } from '@/helpers/install' import { list } from '@/helpers/instance' +import { useSharedInstanceErrors } from '@/helpers/shared-instance-errors' import { useTheming } from '@/store/state' import { parseSharedInstanceInviteNotification } from './shared-instance-invite-parser' @@ -47,6 +48,8 @@ export function useSharedInstanceInviteHandler( const auth = injectAuth() const client = injectModrinthClient() const { handleError } = injectNotificationManager() + const { notifySharedInstanceConnectionError, notifySharedInstanceError } = + useSharedInstanceErrors() const popupNotificationManager = injectPopupNotificationManager() const queryClient = useQueryClient() const router = useRouter() @@ -78,7 +81,10 @@ export function useSharedInstanceInviteHandler( !invite.invitedByUsername && invite.invitedById ? get_user(invite.invitedById, 'bypass').catch(() => null) : null, - client.sharedinstances.instances_v1.get(invite.sharedInstanceId).catch(() => null), + client.sharedinstances.instances_v1.get(invite.sharedInstanceId).catch(() => { + notifySharedInstanceConnectionError() + return null + }), ]) return { @@ -177,7 +183,7 @@ export function useSharedInstanceInviteHandler( () => markNotificationRead(notification), ) } catch (error) { - handleError(toError(error)) + notifySharedInstanceError(error) } } @@ -267,7 +273,7 @@ export function useSharedInstanceInviteHandler( await queryClient.invalidateQueries({ queryKey: ['instances'] }) }) } catch (error) { - handleError(toError(error)) + notifySharedInstanceError(error) } } diff --git a/apps/app-frontend/src/helpers/install.ts b/apps/app-frontend/src/helpers/install.ts index 69d608fd7e..6c92aaa897 100644 --- a/apps/app-frontend/src/helpers/install.ts +++ b/apps/app-frontend/src/helpers/install.ts @@ -102,6 +102,7 @@ export interface SharedInstanceUpdateDiff { } export const SHARED_INSTANCE_UNAVAILABLE_ERROR_CODE = 'shared_instance_unavailable' +export const SHARED_INSTANCES_API_ERROR_CODE = 'shared_instances_api_error' export type SharedInstanceUnavailableReason = 'deleted' | 'access_revoked' | 'quarantined' @@ -113,6 +114,10 @@ export function isSharedInstanceUnavailableError(error: unknown) { return getSharedInstanceUnavailableReason(error) !== null } +export function isSharedInstancesApiError(error: unknown) { + return isRecord(error) && error.code === SHARED_INSTANCES_API_ERROR_CODE +} + export function getSharedInstanceUnavailableReason( error: unknown, ): SharedInstanceUnavailableReason | null { diff --git a/apps/app-frontend/src/helpers/shared-instance-errors.ts b/apps/app-frontend/src/helpers/shared-instance-errors.ts index e1e92efcae..b0ce5d0f74 100644 --- a/apps/app-frontend/src/helpers/shared-instance-errors.ts +++ b/apps/app-frontend/src/helpers/shared-instance-errors.ts @@ -1,6 +1,10 @@ import { defineMessages, injectNotificationManager, useVIntl } from '@modrinth/ui' -import { getErrorMessage, type SharedInstanceUnavailableReason } from '@/helpers/install' +import { + getErrorMessage, + isSharedInstancesApiError, + type SharedInstanceUnavailableReason, +} from '@/helpers/install' export const sharedInstanceErrorMessages = defineMessages({ unavailableTitle: { @@ -39,6 +43,14 @@ export const sharedInstanceErrorMessages = defineMessages({ id: 'instance.shared-instance.error.title', defaultMessage: 'Something has gone wrong', }, + networkErrorTitle: { + id: 'instance.shared-instance.network-error.title', + defaultMessage: 'Network error', + }, + networkErrorText: { + id: 'instance.shared-instance.network-error.text', + defaultMessage: 'Unable to connect to shared instances API', + }, }) export function sharedInstanceUnavailableTextMessage( @@ -82,7 +94,20 @@ export function useSharedInstanceErrors() { }) } + function notifySharedInstanceConnectionError() { + addNotification({ + type: 'error', + title: formatMessage(sharedInstanceErrorMessages.networkErrorTitle), + text: formatMessage(sharedInstanceErrorMessages.networkErrorText), + }) + } + function notifySharedInstanceError(error: unknown) { + if (isSharedInstancesApiError(error)) { + notifySharedInstanceConnectionError() + return + } + addNotification({ type: 'error', title: formatMessage(sharedInstanceErrorMessages.errorTitle), @@ -92,6 +117,7 @@ export function useSharedInstanceErrors() { return { formatSharedInstanceUnavailable, + notifySharedInstanceConnectionError, notifySharedInstanceError, notifySharedInstanceUnavailable, } diff --git a/apps/app-frontend/src/pages/instance/Index.vue b/apps/app-frontend/src/pages/instance/Index.vue index 2ff03c10dc..c8a914a3b8 100644 --- a/apps/app-frontend/src/pages/instance/Index.vue +++ b/apps/app-frontend/src/pages/instance/Index.vue @@ -590,17 +590,16 @@ const startInstance = async (context: string) => { !!instance.value.shared_instance && !sharedInstanceActionsLocked.value && !offline.value if (canCheckSharedInstanceUpdate) { - let preview: Awaited> + let preview: Awaited> = null checkingSharedInstanceLaunch.value = true try { preview = await refreshSharedInstanceUpdatePreview() } catch (error) { if (isSharedInstanceUnavailableError(error)) { await handleSharedInstanceUnavailable(getSharedInstanceUnavailableReason(error)) - } else { - notifySharedInstanceError(error) + return } - return + notifySharedInstanceError(error) } finally { checkingSharedInstanceLaunch.value = false } @@ -707,7 +706,7 @@ async function reportSharedInstance(event?: MouseEvent, closeUpdateModal = false if (closeUpdateModal) sharedInstanceUpdateModal.value?.hide() sharedInstanceReportModal.value?.showReport(preview, event) } catch (error) { - handleError(error as Error) + notifySharedInstanceError(error) } } diff --git a/apps/app-frontend/src/pages/instance/share/index.vue b/apps/app-frontend/src/pages/instance/share/index.vue index f13b573920..3aededf19e 100644 --- a/apps/app-frontend/src/pages/instance/share/index.vue +++ b/apps/app-frontend/src/pages/instance/share/index.vue @@ -47,7 +47,13 @@ @state-change="publishState = $event" /> -