feat(storage): point a full trial account at the upgrade rather than at nothing

Being told "storage limit exceeded" when the limit is the free trial's three
gigabytes is a dead end. The account is not full because it stores a lot; it is
capped because it has not subscribed, and deleting files buys back very little.

The refusal now says which ceiling it is and carries its own error code, so the
toast can offer a link to the billing settings on the trial ceiling and stay
quiet on the paid one, where subscribing changes nothing.

The code had to survive the trip to the toast, which meant the upload helpers
throwing something that carries it rather than a bare Error. Two places were
dropping the server's message on the floor entirely: adding a version reported
"Failed to initialize upload" whatever the server said, and every asset upload
in the pane rewrote its own failure text.
This commit is contained in:
2026-08-18 11:42:30 +03:00
parent 63288761ed
commit 6561e0817e
10 changed files with 239 additions and 40 deletions
@@ -3,6 +3,7 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { toast } from 'sonner';
import type { VideoAsset } from '@/components/video-page/types';
import { apiRequestError, toastApiError } from '@/lib/client/api-error';
type BunnyDownloadPreference = 'original' | 'compressed';
@@ -43,6 +44,7 @@ interface AssetsListResponse {
interface AssetCreateResponse {
data?: VideoAsset;
error?: string;
code?: string;
}
const ASSET_PAGE_SIZE = 40;
@@ -185,7 +187,7 @@ export function useVideoAssets({
});
const body = (await res.json().catch(() => null)) as AssetCreateResponse | null;
if (!res.ok || !body?.data) {
toast.error(body?.error || 'Failed to create asset');
toastApiError(body, 'Failed to create asset');
return null;
}
@@ -288,7 +290,7 @@ export function useVideoAssets({
} | null;
const token = payload?.data?.token;
if (!response.ok || !token) {
throw new Error(payload?.error || 'Failed to prepare upload');
throw apiRequestError(payload, 'Failed to prepare upload');
}
return token;
},