frontend: uuidv4 for local ids in place of Math.random (Sonar S2245)

This commit is contained in:
Dylan M. Taylor committed 2026-11-10 23:39:43 +00:00
1 parent 71adfd4701
commit 8653c413cd
18 files changed
+60 -83

No files matched your search

@@ -69,10 +69,7 @@ const formatTime = (timestamp: number) => {
return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
};
const generateId = () =>
typeof crypto !== "undefined" && crypto.randomUUID
? crypto.randomUUID()
: Math.random().toString(36).slice(2, 10);
const generateId = () => uuidv4();
const WORLD_MODEL_PAGE = ModelPage.ImageTo3DWorld;
const OBJECT_MODEL_PAGE = ModelPage.ImageTo3DObject;
@@ -39,6 +39,7 @@ import {
type GalleryItem,
} from "@storyteller/ui-generation-list";
import { useTabStore } from "~/pages/Stores/TabState";
import { v4 as uuidv4 } from "uuid";
// Media item actions shared by the TopBar gallery modal / lightbox wiring and
// the create pages' generation feed (rows + cards). All of them operate on
@@ -130,7 +131,7 @@ export async function downloadMediaFilesToFolder(
export async function applyMakeVideoFromImage(url: string, mediaToken?: string) {
try {
const referenceImage: RefImage = {
id: Math.random().toString(36).substring(7),
id: uuidv4(),
url,
file: new File([], "library-image"),
mediaToken: mediaToken || "",
@@ -178,7 +179,7 @@ export function applyRecreateFromPromptData(data: {
for (const ci of contextImages) {
const base = {
id: Math.random().toString(36).substring(7),
id: uuidv4(),
url: ci.media_links.cdn_url,
mediaToken: ci.media_token,
};
@@ -11,6 +11,7 @@ import { CostCalculatorButton } from "@storyteller/ui-pricing-modal";
import { ModelPage } from "@storyteller/ui-model-selector";
import { UploadEntryCard } from "~/components/media/UploadEntryCard";
import { BlankCanvasModal, type BaseSelectorImage } from "@storyteller/ui-pagedraw";
import { v4 as uuidv4 } from "uuid";
const MAX_GALLERY_SELECTIONS = 1;
@@ -64,7 +65,7 @@ export const BaseImageSelector = ({
setIsBlankCanvasModalOpen(false);
const blankImage: BaseSelectorImage = {
url: "",
mediaToken: `blank_canvas_${width}x${height}_${Math.random().toString(36).substring(2, 8)}`,
mediaToken: `blank_canvas_${width}x${height}_${uuidv4()}`,
isBlankCanvas: true,
blankCanvasWidth: width,
blankCanvasHeight: height,
@@ -79,7 +80,7 @@ export const BaseImageSelector = ({
const reader = new FileReader();
reader.onloadend = () => {
uploadImage({
title: `reference-image-${Math.random().toString(36).substring(2, 15)}`,
title: `reference-image-${uuidv4()}`,
assetFile: file,
progressCallback: (newState) => {
if (newState.status === UploaderStates.success && newState.data) {
@@ -1,6 +1,7 @@
import { create } from "zustand";
import type { GeneratedImage } from "@storyteller/tauri-events";
import type { ModelCreator } from "@storyteller/model-list";
import { v4 as uuidv4 } from "uuid";
export type TextToImageBatch = {
id: string;
@@ -43,11 +44,7 @@ export const useTextToImageStore = create<TextToImageState>((set, get) => ({
subscriberId?: string,
modelCreator?: ModelCreator,
) => {
const id = subscriberId
? subscriberId
: crypto.randomUUID
? crypto.randomUUID()
: Math.random().toString(36).slice(2);
const id = subscriberId || uuidv4();
const batch: TextToImageBatch = {
id,
prompt,
@@ -72,11 +72,7 @@ export const useImageTo3DStore = create<ImageTo3DState>((set, get) => ({
meshOnly: boolean,
subscriberId?: string,
) => {
const id = subscriberId
? subscriberId
: crypto.randomUUID
? crypto.randomUUID()
: Math.random().toString(36).slice(2);
const id = subscriberId || uuidv4();
const result: ImageTo3DResult = {
id,
mode,
@@ -111,8 +107,7 @@ export const useImageTo3DStore = create<ImageTo3DState>((set, get) => ({
: -1;
if (targetIdx === -1) {
const generatedId =
crypto.randomUUID?.() ?? Math.random().toString(36).slice(2);
const generatedId = uuidv4();
const result: ImageTo3DResult = {
id: generatedId,
subscriberId: generatedId,
@@ -54,11 +54,7 @@ export const useImageTo3DWorldStore = create<ImageTo3DWorldState>(
previewUrl: string | undefined,
subscriberId?: string,
) => {
const id = subscriberId
? subscriberId
: crypto.randomUUID
? crypto.randomUUID()
: Math.random().toString(36).slice(2);
const id = subscriberId || uuidv4();
const result: ImageTo3DWorldResult = {
id,
mode,
@@ -92,8 +88,7 @@ export const useImageTo3DWorldStore = create<ImageTo3DWorldState>(
: -1;
if (targetIdx === -1) {
const generatedId =
crypto.randomUUID?.() ?? Math.random().toString(36).slice(2);
const generatedId = uuidv4();
const result: ImageTo3DWorldResult = {
id: generatedId,
subscriberId: generatedId,
@@ -1,4 +1,5 @@
import { create } from "zustand";
import { v4 as uuidv4 } from "uuid";
export type VideoItem = {
media_token: string;
@@ -33,11 +34,7 @@ type ImageToVideoState = {
export const useImageToVideoStore = create<ImageToVideoState>((set, get) => ({
batches: [],
startBatch: (prompt: string, modelLabel: string, subscriberId?: string) => {
const id = subscriberId
? subscriberId
: crypto.randomUUID
? crypto.randomUUID()
: Math.random().toString(36).slice(2);
const id = subscriberId || uuidv4();
const batch: ImageToVideoBatch = {
id,
prompt,
@@ -61,7 +58,7 @@ export const useImageToVideoStore = create<ImageToVideoState>((set, get) => ({
? s.batches.findIndex((b) => b.id === pending.id)
: -1;
if (idx === -1) {
const id = Math.random().toString(36).slice(2);
const id = uuidv4();
const batch: ImageToVideoBatch = {
id,
prompt,
@@ -420,7 +420,7 @@ export const VideoFrameExtractor = () => {
const file = new File([blob], fileName, { type: "image/png" });
const referenceImage: RefImage = {
id: Math.random().toString(36).substring(7),
id: uuidv4(),
url: frameUrl,
file: file,
mediaToken: uploadResponse.data,
@@ -8,6 +8,7 @@ import {
isActionReminderOpen,
showActionReminder,
} from "@storyteller/ui-action-reminder-modal";
import { v4 as uuidv4 } from "uuid";
export type { ImageBundle };
@@ -53,7 +54,7 @@ export const HistoryStack = ({
// This is used to force image reloads in different sessions
// and prevent fetching CORS-tainted images from cache
const sessionRandBuster = useRef(Math.random());
const sessionRandBuster = useRef(uuidv4());
const scrollRef = useRef<HTMLDivElement | null>(null);
const prevPendingCountRef = useRef<number>(0);
@@ -50,6 +50,7 @@ import {
} from "./components/Model3DOverlay";
import { CameraIcon, Maximize2Icon, RefreshCwIcon } from "lucide-react";
import type { PageDrawAdapter } from "./adapter";
import { v4 as uuidv4 } from "uuid";
const PAGE_ID: ModelPage = ModelPage.Canvas2D;
@@ -857,7 +858,7 @@ const PageDraw = ({
const subscriberId: string =
crypto?.randomUUID?.() ??
`inpaint-${Date.now()}-${Math.random().toString(36).slice(2)}`;
`inpaint-${Date.now()}-${uuidv4()}`;
adapter.onEnqueueMeta?.({
prompt,
@@ -44,6 +44,7 @@ import {
import { CSS } from "@dnd-kit/utilities";
import type { UploadMediaFn } from "@storyteller/api";
import { v4 as uuidv4 } from "uuid";
export type { UploadMediaFn as UploadImageFn } from "@storyteller/api";
type UploadImageFn = UploadMediaFn;
@@ -448,19 +449,17 @@ export const ImagePromptRow = ({
break;
}
const uploadId = Math.random().toString(36).substring(7);
const uploadId = uuidv4();
setUploadingVideo({ id: uploadId, file });
if (uploadVideo) {
await uploadVideo({
title: `reference-video-${Math.random()
.toString(36)
.substring(2, 15)}`,
title: `reference-video-${uuidv4()}`,
assetFile: file,
progressCallback: (newState) => {
if (newState.status === UploaderStates.success && newState.data) {
const refVideo: RefVideo = {
id: Math.random().toString(36).substring(7),
id: uuidv4(),
url: URL.createObjectURL(file),
file,
mediaToken: newState.data,
@@ -480,7 +479,7 @@ export const ImagePromptRow = ({
});
} else {
const refVideo: RefVideo = {
id: Math.random().toString(36).substring(7),
id: uuidv4(),
url: URL.createObjectURL(file),
file,
mediaToken: "",
@@ -562,19 +561,17 @@ export const ImagePromptRow = ({
break;
}
const uploadId = Math.random().toString(36).substring(7);
const uploadId = uuidv4();
setUploadingAudio({ id: uploadId, file });
if (uploadAudio) {
await uploadAudio({
title: `reference-audio-${Math.random()
.toString(36)
.substring(2, 15)}`,
title: `reference-audio-${uuidv4()}`,
assetFile: file,
progressCallback: (newState) => {
if (newState.status === UploaderStates.success && newState.data) {
const refAudio: RefAudio = {
id: Math.random().toString(36).substring(7),
id: uuidv4(),
url: URL.createObjectURL(file),
file,
mediaToken: newState.data,
@@ -592,7 +589,7 @@ export const ImagePromptRow = ({
});
} else {
const refAudio: RefAudio = {
id: Math.random().toString(36).substring(7),
id: uuidv4(),
url: URL.createObjectURL(file),
file,
mediaToken: "",
@@ -629,7 +626,7 @@ export const ImagePromptRow = ({
: files.slice(0, availableSlots);
filesToProcess.forEach(async (file) => {
const uploadId = Math.random().toString(36).substring(7);
const uploadId = uuidv4();
if (uploadTarget === "end") {
setUploadingEnd({ id: uploadId, file, previewUrl: "" });
} else {
@@ -681,9 +678,7 @@ export const ImagePromptRow = ({
if (uploadImage) {
await uploadImage({
title: `reference-image-${Math.random()
.toString(36)
.substring(2, 15)}`,
title: `reference-image-${uuidv4()}`,
assetFile: file,
progressCallback: (newState) => {
if (newState.status === UploaderStates.success && newState.data) {
@@ -767,7 +762,7 @@ export const ImagePromptRow = ({
}
currentTotal += duration;
newVideos.push({
id: Math.random().toString(36).substring(7),
id: uuidv4(),
url: item.fullImage,
file: new File([], "library-video"),
mediaToken: item.id,
@@ -794,7 +789,7 @@ export const ImagePromptRow = ({
const item = selectedItems[0];
if (item && item.fullImage) {
setEndFrameImage?.({
id: Math.random().toString(36).substring(7),
id: uuidv4(),
url: item.fullImage,
file: new File([], "library-image"),
mediaToken: item.id,
@@ -818,7 +813,7 @@ export const ImagePromptRow = ({
selectedItems.slice(0, availableSlots).forEach((item) => {
if (!item.fullImage) return;
newRefs.push({
id: Math.random().toString(36).substring(7),
id: uuidv4(),
url: item.fullImage,
file: new File([], "library-image"),
mediaToken: item.id,
@@ -47,6 +47,7 @@ import {
import { AudioTuningPopover } from "./common/AudioTuningPopover";
import { SoundsSettingsPopover } from "./common/SoundsSettingsPopover";
import { StylePromptRow } from "./common/StylePromptRow";
import { v4 as uuidv4 } from "uuid";
const DEFAULT_AUDIO_MODEL_ID = "suno_music";
@@ -273,7 +274,7 @@ export const PromptBoxAudio = ({
}
total += duration;
added.push({
id: Math.random().toString(36).substring(7),
id: uuidv4(),
url: item.fullImage,
file: new File([], "library-audio"),
mediaToken: item.id,
@@ -36,6 +36,7 @@ import {
type DroppedFiles,
} from "./deck/usePromptBoxDrop";
import { DeckAddAction, DeckItem } from "./deck/deckTypes";
import { v4 as uuidv4 } from "uuid";
interface PromptBoxImageProps {
useJobContext: () => JobContextType;
@@ -92,7 +93,7 @@ export const PromptBoxImage = ({
useEffect(() => {
if (imageMediaId && url) {
const referenceImage: RefImage = {
id: Math.random().toString(36).substring(7),
id: uuidv4(),
url: url,
file: new File([], "library-image"),
mediaToken: imageMediaId,
@@ -276,7 +277,7 @@ export const PromptBoxImage = ({
useEffect(() => {
if (imageMediaId && url) {
const referenceImage: RefImage = {
id: Math.random().toString(36).substring(7),
id: uuidv4(),
url: url,
file: new File([], "library-image"),
mediaToken: imageMediaId,
@@ -376,9 +377,7 @@ export const PromptBoxImage = ({
gtagEvent("enqueue_image");
const subscriberId = crypto.randomUUID
? crypto.randomUUID()
: Math.random().toString(36).slice(2);
const subscriberId = uuidv4();
setTimeout(() => {
console.debug("Turn off blocking of prompt box...");
@@ -59,6 +59,7 @@ import {
} from "./PromptFullscreenModal";
import { PromptFullscreenButton } from "./PromptFullscreenButton";
import { PromptClearAllButton } from "./PromptClearAllButton";
import { v4 as uuidv4 } from "uuid";
declare global {
interface Window {
@@ -128,7 +129,7 @@ export const PromptBoxVideo = ({
useEffect(() => {
if (imageMediaId && url) {
const referenceImage: RefImage = {
id: Math.random().toString(36).substring(7),
id: uuidv4(),
url: url,
file: new File([], "library-image"),
mediaToken: imageMediaId,
@@ -333,7 +334,7 @@ export const PromptBoxVideo = ({
useEffect(() => {
if (imageMediaId && url) {
const referenceImage: RefImage = {
id: Math.random().toString(36).substring(7),
id: uuidv4(),
url: url,
file: new File([], "library-image"),
mediaToken: imageMediaId,
@@ -1217,9 +1218,7 @@ export const PromptBoxVideo = ({
const enqueuePromises: Promise<unknown>[] = [];
for (let i = 0; i < count; i++) {
const subscriberId = crypto.randomUUID
? crypto.randomUUID()
: Math.random().toString(36).slice(2);
const subscriberId = uuidv4();
subscriberIds.push(subscriberId);
enqueuePromises.push(GenerateVideo(buildRequest(subscriberId)));
}
@@ -16,6 +16,7 @@ import {
AUDIO_FILE_TYPE_ERROR,
isAudioFile,
} from "./audioFiles";
import { v4 as uuidv4 } from "uuid";
/** Lets the prompt box push dropped/pasted files through the same upload
* path the row's own file inputs use. */
@@ -104,12 +105,12 @@ export const AudioReferenceRow = forwardRef<
if (uploadAudio) {
setIsUploadingAudio(true);
await uploadAudio({
title: `reference-audio-${Math.random().toString(36).substring(2, 15)}`,
title: `reference-audio-${uuidv4()}`,
assetFile: file,
progressCallback: (newState) => {
if (newState.status === UploaderStates.success && newState.data) {
const refAudio: RefAudio = {
id: Math.random().toString(36).substring(7),
id: uuidv4(),
url: URL.createObjectURL(file),
file,
mediaToken: newState.data,
@@ -145,12 +146,12 @@ export const AudioReferenceRow = forwardRef<
setIsUploadingImage(true);
await uploadImage({
title: `reference-image-${Math.random().toString(36).substring(2, 15)}`,
title: `reference-image-${uuidv4()}`,
assetFile: file,
progressCallback: (newState) => {
if (newState.status === UploaderStates.success && newState.data) {
const refImage: RefImage = {
id: Math.random().toString(36).substring(7),
id: uuidv4(),
url: URL.createObjectURL(file),
file,
mediaToken: newState.data,
@@ -17,6 +17,7 @@ import {
type VFXModelId,
type VFXResolution,
} from "./types";
import { v4 as uuidv4 } from "uuid";
export type VFXUploadFn = (args: {
title: string;
@@ -95,12 +96,12 @@ export const PromptBoxVFX = ({
setSourceUploading(true);
await uploadVideo({
title: `vfx-source-${Math.random().toString(36).slice(2, 10)}`,
title: `vfx-source-${uuidv4()}`,
assetFile: file,
progressCallback: (state) => {
if (state.status === UploaderStates.success && state.data) {
setSource({
id: Math.random().toString(36).slice(2),
id: uuidv4(),
url: previewUrl,
mediaToken: state.data,
});
@@ -136,12 +137,12 @@ export const PromptBoxVFX = ({
setUploading(true);
await uploadImage({
title: `vfx-${kind}-${Math.random().toString(36).slice(2, 10)}`,
title: `vfx-${kind}-${uuidv4()}`,
assetFile: file,
progressCallback: (state) => {
if (state.status === UploaderStates.success && state.data) {
setRef({
id: Math.random().toString(36).slice(2),
id: uuidv4(),
url: previewUrl,
mediaToken: state.data,
});
@@ -183,7 +184,7 @@ export const PromptBoxVFX = ({
return;
}
const ref = {
id: Math.random().toString(36).slice(2),
id: uuidv4(),
url: item.fullImage || item.thumbnail || "",
mediaToken: item.id,
};
+2 -4
View File
@@ -1,4 +1,5 @@
import { StorytellerApiHostStore } from "@storyteller/api";
import { v4 as uuidv4 } from "uuid";
export type SubmitVFXJobRequest = {
source_video_media_token: string;
@@ -64,8 +65,5 @@ export async function submitVFXJob(
}
export function newIdempotencyToken(): string {
if (typeof crypto !== "undefined" && crypto.randomUUID) {
return crypto.randomUUID();
}
return Math.random().toString(36).slice(2) + Date.now().toString(36);
return uuidv4();
}
@@ -9,6 +9,7 @@ import {
type VFXResult,
type VFXSubTab,
} from "./types";
import { v4 as uuidv4 } from "uuid";
type VFXState = {
subTab: VFXSubTab;
@@ -90,10 +91,7 @@ export const useVFXStore = create<VFXState>()(
startResult: () => {
const { prompt, resolution, source, mask, reference } = get();
const id =
typeof crypto !== "undefined" && crypto.randomUUID
? crypto.randomUUID()
: Math.random().toString(36).slice(2);
const id = uuidv4();
const result: VFXResult = {
id,
status: "pending",