mirror of
https://github.com/yusufipk/OpenFrame.git
synced 2026-09-11 09:36:08 +00:00
fix(upload): give a full trial account the upgrade link where it reads the refusal
The video uploader threw the API error away and toasted the bare message, so the one caller most likely to hit the trial storage ceiling was the one that lost the way out of it. Route it through toastApiError, which keeps the error code and attaches the action, and repeat the link in the queue row so it survives the toast timing out. The button now says Upgrade rather than See plans, matching the verb the message itself uses.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { CheckCircle2, Loader2, UploadCloud, XCircle } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
@@ -22,6 +23,7 @@ import {
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { resolvePublicBunnyCdnHostname } from '@/lib/bunny-cdn';
|
||||
import { isTrialStorageError, toastApiError } from '@/lib/client/api-error';
|
||||
import {
|
||||
cleanupPendingProjectUpload,
|
||||
getDefaultTitleFromFile,
|
||||
@@ -46,6 +48,8 @@ type QueueItem = {
|
||||
status: QueueItemStatus;
|
||||
progress: number;
|
||||
error?: string;
|
||||
/** The failure was the trial ceiling, so the row shows the way out. */
|
||||
errorIsTrialLimit?: boolean;
|
||||
};
|
||||
|
||||
interface VideoDragDropUploaderProps {
|
||||
@@ -306,12 +310,17 @@ export function VideoDragDropUploader({
|
||||
failCount += 1;
|
||||
|
||||
const message = error instanceof Error ? error.message : 'Failed to upload video';
|
||||
const isTrialLimit = isTrialStorageError(error);
|
||||
setQueue((prev) =>
|
||||
prev.map((entry) =>
|
||||
entry.id === item.id ? { ...entry, status: 'error', error: message } : entry
|
||||
entry.id === item.id
|
||||
? { ...entry, status: 'error', error: message, errorIsTrialLimit: isTrialLimit }
|
||||
: entry
|
||||
)
|
||||
);
|
||||
toast.error(`${item.file.name}: ${message}`);
|
||||
// Keeps the error code alive to the toast: a trial account that has run
|
||||
// out of room is shown the plan rather than just told the upload failed.
|
||||
toastApiError(error, 'Failed to upload video', { prefix: item.file.name });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,7 +509,17 @@ export function VideoDragDropUploader({
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate font-medium">{item.file.name}</p>
|
||||
{item.error ? (
|
||||
<p className="truncate text-xs text-destructive">{item.error}</p>
|
||||
<p className="text-xs text-destructive">
|
||||
<span className="align-middle">{item.error}</span>
|
||||
{item.errorIsTrialLimit && (
|
||||
<Link
|
||||
href="/settings"
|
||||
className="ml-1 align-middle font-medium underline underline-offset-2"
|
||||
>
|
||||
Upgrade
|
||||
</Link>
|
||||
)}
|
||||
</p>
|
||||
) : (
|
||||
<p className="truncate text-xs text-muted-foreground">
|
||||
{getDefaultTitleFromFile(item.file)}
|
||||
|
||||
+12
-2
@@ -42,6 +42,14 @@ export function apiRequestError(
|
||||
return new ApiRequestError(payload?.error || fallback, payload?.code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this failure is the trial ceiling, for callers that draw the way out
|
||||
* themselves rather than handing it to `toastApiError`.
|
||||
*/
|
||||
export function isTrialStorageError(source: unknown): boolean {
|
||||
return codeOf(source) === API_ERROR_CODES.TRIAL_STORAGE_LIMIT_EXCEEDED;
|
||||
}
|
||||
|
||||
function codeOf(source: unknown): string | null {
|
||||
if (source instanceof ApiRequestError) return source.code;
|
||||
if (source && typeof source === 'object' && 'code' in source) {
|
||||
@@ -82,13 +90,15 @@ export function toastApiError(
|
||||
const message = messageOf(source, fallback);
|
||||
const text = options.prefix ? `${options.prefix}: ${message}` : message;
|
||||
|
||||
if (codeOf(source) === API_ERROR_CODES.TRIAL_STORAGE_LIMIT_EXCEEDED) {
|
||||
if (isTrialStorageError(source)) {
|
||||
toast.error(text, {
|
||||
// Longer than a plain error: this one is asking for a decision rather than
|
||||
// just reporting, and it disappears under the cursor at the usual timing.
|
||||
duration: 12000,
|
||||
action: {
|
||||
label: 'See plans',
|
||||
// The message ends in "Upgrade to get 200 GB", so the button is the verb
|
||||
// that sentence just used rather than a second name for the same thing.
|
||||
label: 'Upgrade',
|
||||
onClick: () => {
|
||||
window.location.href = '/settings';
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user