Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e28850ca6a | ||
|
|
ab53f7a7de | ||
|
|
e389ab1433 | ||
|
|
c250349261 | ||
|
|
7170a18dd7 | ||
|
|
60d5fff490 | ||
|
|
e124d1f325 |
@@ -20,6 +20,7 @@ import apiClient from "@app/services/apiClient";
|
||||
import {
|
||||
parseContentDispositionFilename,
|
||||
extractLatestFilesFromBundle,
|
||||
readResponseHeader,
|
||||
} from "@app/services/shareBundleUtils";
|
||||
import { truncateCenter } from "@app/utils/textUtils";
|
||||
import { generateThumbnailForFile } from "@app/utils/thumbnailUtils";
|
||||
@@ -270,14 +271,8 @@ export function FileSelectorPicker({
|
||||
skipAuthRedirect: true,
|
||||
} as any,
|
||||
);
|
||||
const ct =
|
||||
res.headers?.["content-type"] ||
|
||||
res.headers?.["Content-Type"] ||
|
||||
"";
|
||||
const disp =
|
||||
res.headers?.["content-disposition"] ||
|
||||
res.headers?.["Content-Disposition"] ||
|
||||
"";
|
||||
const ct = readResponseHeader(res.headers, "content-type");
|
||||
const disp = readResponseHeader(res.headers, "content-disposition");
|
||||
const files = await extractLatestFilesFromBundle(
|
||||
res.data as Blob,
|
||||
parseContentDispositionFilename(disp) || "shared-file",
|
||||
@@ -294,14 +289,8 @@ export function FileSelectorPicker({
|
||||
skipAuthRedirect: true,
|
||||
} as any,
|
||||
);
|
||||
const ct =
|
||||
res.headers?.["content-type"] ||
|
||||
res.headers?.["Content-Type"] ||
|
||||
"";
|
||||
const disp =
|
||||
res.headers?.["content-disposition"] ||
|
||||
res.headers?.["Content-Disposition"] ||
|
||||
"";
|
||||
const ct = readResponseHeader(res.headers, "content-type");
|
||||
const disp = readResponseHeader(res.headers, "content-disposition");
|
||||
const files = await extractLatestFilesFromBundle(
|
||||
res.data as Blob,
|
||||
parseContentDispositionFilename(disp) || stub.name,
|
||||
|
||||
@@ -22,6 +22,7 @@ import { alert } from "@app/components/toast";
|
||||
import {
|
||||
extractLatestFilesFromBundle,
|
||||
parseContentDispositionFilename,
|
||||
readResponseHeader,
|
||||
} from "@app/services/shareBundleUtils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { openFilesFromDisk } from "@app/services/openFilesFromDisk";
|
||||
@@ -1003,16 +1004,14 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
|
||||
skipAuthRedirect: true,
|
||||
} as any,
|
||||
);
|
||||
const contentType =
|
||||
(response.headers &&
|
||||
(response.headers["content-type"] ||
|
||||
response.headers["Content-Type"])) ||
|
||||
"";
|
||||
const disposition =
|
||||
(response.headers &&
|
||||
(response.headers["content-disposition"] ||
|
||||
response.headers["Content-Disposition"])) ||
|
||||
"";
|
||||
const contentType = readResponseHeader(
|
||||
response.headers,
|
||||
"content-type",
|
||||
);
|
||||
const disposition = readResponseHeader(
|
||||
response.headers,
|
||||
"content-disposition",
|
||||
);
|
||||
const filename =
|
||||
parseContentDispositionFilename(disposition) ||
|
||||
file.name ||
|
||||
@@ -1270,6 +1269,3 @@ export const useFileManagerContext = (): FileManagerContextValue => {
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
// Export the context for advanced use cases
|
||||
export { FileManagerContext };
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
isZipBundle,
|
||||
loadShareBundleEntries,
|
||||
parseContentDispositionFilename,
|
||||
readResponseHeader,
|
||||
} from "@app/services/shareBundleUtils";
|
||||
|
||||
interface FilesModalContextType {
|
||||
@@ -184,16 +185,11 @@ export const FilesModalProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||
skipAuthRedirect: true,
|
||||
} as any,
|
||||
);
|
||||
const contentType =
|
||||
(response.headers &&
|
||||
(response.headers["content-type"] ||
|
||||
response.headers["Content-Type"])) ||
|
||||
"";
|
||||
const disposition =
|
||||
(response.headers &&
|
||||
(response.headers["content-disposition"] ||
|
||||
response.headers["Content-Disposition"])) ||
|
||||
"";
|
||||
const contentType = readResponseHeader(response.headers, "content-type");
|
||||
const disposition = readResponseHeader(
|
||||
response.headers,
|
||||
"content-disposition",
|
||||
);
|
||||
const filename =
|
||||
parseContentDispositionFilename(disposition) || "server-file";
|
||||
const blob = response.data as Blob;
|
||||
@@ -210,16 +206,11 @@ export const FilesModalProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||
skipAuthRedirect: true,
|
||||
} as any,
|
||||
);
|
||||
const contentType =
|
||||
(response.headers &&
|
||||
(response.headers["content-type"] ||
|
||||
response.headers["Content-Type"])) ||
|
||||
"";
|
||||
const disposition =
|
||||
(response.headers &&
|
||||
(response.headers["content-disposition"] ||
|
||||
response.headers["Content-Disposition"])) ||
|
||||
"";
|
||||
const contentType = readResponseHeader(response.headers, "content-type");
|
||||
const disposition = readResponseHeader(
|
||||
response.headers,
|
||||
"content-disposition",
|
||||
);
|
||||
const filename =
|
||||
parseContentDispositionFilename(disposition) || "shared-file";
|
||||
const blob = response.data as Blob;
|
||||
|
||||
@@ -4,6 +4,48 @@ import type { ShareBundleManifest } from "@app/services/serverStorageBundle";
|
||||
|
||||
const MANIFEST_FILENAME = "stirling-share.json";
|
||||
|
||||
type HeaderLike = {
|
||||
get?: (name: string) => unknown;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
function headerValueToString(value: unknown): string {
|
||||
if (typeof value === "string") return value;
|
||||
if (typeof value === "number" || typeof value === "boolean") {
|
||||
return String(value);
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
const firstString = value.find(
|
||||
(entry): entry is string => typeof entry === "string",
|
||||
);
|
||||
return firstString ?? "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function toHeaderKey(name: string): string {
|
||||
return name.replace(
|
||||
/(^|-)([a-z])/g,
|
||||
(_, prefix: string, char: string) => prefix + char.toUpperCase(),
|
||||
);
|
||||
}
|
||||
|
||||
export function readResponseHeader(headers: unknown, name: string): string {
|
||||
if (!headers || typeof headers !== "object") {
|
||||
return "";
|
||||
}
|
||||
|
||||
const typedHeaders = headers as HeaderLike;
|
||||
if (typeof typedHeaders.get === "function") {
|
||||
return headerValueToString(typedHeaders.get(name));
|
||||
}
|
||||
|
||||
return (
|
||||
headerValueToString(typedHeaders[name]) ||
|
||||
headerValueToString(typedHeaders[toHeaderKey(name)])
|
||||
);
|
||||
}
|
||||
|
||||
export function parseContentDispositionFilename(
|
||||
disposition?: string,
|
||||
): string | null {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import apiClient from "@app/services/apiClient";
|
||||
import type { SavedSignature } from "@app/types/signature";
|
||||
import { readResponseHeader } from "@app/services/shareBundleUtils";
|
||||
|
||||
export type StorageType = "backend" | "localStorage";
|
||||
|
||||
@@ -166,7 +167,9 @@ class SignatureStorageService {
|
||||
|
||||
// Convert to data URL (base64) for both display and use
|
||||
const blob = new Blob([imageResponse.data], {
|
||||
type: imageResponse.headers["content-type"] || "image/png",
|
||||
type:
|
||||
readResponseHeader(imageResponse.headers, "content-type") ||
|
||||
"image/png",
|
||||
});
|
||||
|
||||
const dataUrl = await new Promise<string>((resolve, reject) => {
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
isZipBundle,
|
||||
loadShareBundleEntries,
|
||||
parseContentDispositionFilename,
|
||||
readResponseHeader,
|
||||
} from "@app/services/shareBundleUtils";
|
||||
|
||||
interface ShareLinkLoaderProps {
|
||||
@@ -78,16 +79,14 @@ export default function ShareLinkLoader({ token }: ShareLinkLoaderProps) {
|
||||
);
|
||||
if (signal.aborted) return;
|
||||
|
||||
const contentType =
|
||||
(response.headers &&
|
||||
(response.headers["content-type"] ||
|
||||
response.headers["Content-Type"])) ||
|
||||
"";
|
||||
const disposition =
|
||||
(response.headers &&
|
||||
(response.headers["content-disposition"] ||
|
||||
response.headers["Content-Disposition"])) ||
|
||||
"";
|
||||
const contentType = readResponseHeader(
|
||||
response.headers,
|
||||
"content-type",
|
||||
);
|
||||
const disposition = readResponseHeader(
|
||||
response.headers,
|
||||
"content-disposition",
|
||||
);
|
||||
const filename =
|
||||
parseContentDispositionFilename(disposition) || "shared-file";
|
||||
const blob = response.data as Blob;
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
isZipBundle,
|
||||
loadShareBundleEntries,
|
||||
parseContentDispositionFilename,
|
||||
readResponseHeader,
|
||||
} from "@app/services/shareBundleUtils";
|
||||
|
||||
export interface ShareLinkMetadata {
|
||||
@@ -44,15 +45,11 @@ export async function downloadShareLink(token: string): Promise<{
|
||||
suppressErrorToast: true,
|
||||
skipAuthRedirect: true,
|
||||
});
|
||||
const contentType =
|
||||
(response.headers &&
|
||||
(response.headers["content-type"] || response.headers["Content-Type"])) ||
|
||||
"";
|
||||
const disposition =
|
||||
(response.headers &&
|
||||
(response.headers["content-disposition"] ||
|
||||
response.headers["Content-Disposition"])) ||
|
||||
"";
|
||||
const contentType = readResponseHeader(response.headers, "content-type");
|
||||
const disposition = readResponseHeader(
|
||||
response.headers,
|
||||
"content-disposition",
|
||||
);
|
||||
const filename =
|
||||
parseContentDispositionFilename(disposition) || "shared-file";
|
||||
const blob = response.data as Blob;
|
||||
|
||||
Generated
+408
-618
File diff suppressed because it is too large
Load Diff
@@ -150,7 +150,7 @@
|
||||
"puppeteer": "^24.25.0",
|
||||
"rollup-plugin-visualizer": "^7.0.1",
|
||||
"storybook": "^9.1.20",
|
||||
"tsx": "^4.21.0",
|
||||
"tsx": "^4.22.4",
|
||||
"typescript": "^6.0.3",
|
||||
"typescript-eslint": "^8.61.1",
|
||||
"vite": "^7.3.2",
|
||||
|
||||
Reference in New Issue
Block a user