mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-30 19:22:10 +03:00
fix(frontend): keep the given file name on mobile downloads
FileManager typed every download text/plain. Android's MediaStore appends the MIME type's extension whenever the name's own extension maps elsewhere, so a subscriber saving a WireGuard config got peer.conf.txt, which the WireGuard app refuses; .json, .yaml and .log downloads were renamed the same way. Desktop browsers honour the download name, which is why only phones saw it. application/octet-stream carries no extension of its own, so the name the panel chose is kept.
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { FileManager } from '@/utils';
|
||||
|
||||
const { createObjectURL, revokeObjectURL } = URL;
|
||||
|
||||
afterEach(() => {
|
||||
URL.createObjectURL = createObjectURL;
|
||||
URL.revokeObjectURL = revokeObjectURL;
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('FileManager.downloadTextFile', () => {
|
||||
// Android's MediaStore appends the blob type's own extension when the name's
|
||||
// extension maps elsewhere, so a text/plain peer.conf lands as peer.conf.txt.
|
||||
it('hands the browser an untyped blob so a mobile save keeps the given name', () => {
|
||||
const blobs: Blob[] = [];
|
||||
URL.createObjectURL = vi.fn((blob: Blob) => {
|
||||
blobs.push(blob);
|
||||
return 'blob:download';
|
||||
});
|
||||
URL.revokeObjectURL = vi.fn();
|
||||
let savedAs = '';
|
||||
vi.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(function (
|
||||
this: HTMLAnchorElement,
|
||||
) {
|
||||
savedAs = this.download;
|
||||
});
|
||||
|
||||
FileManager.downloadTextFile('[Interface]\n', 'alice.conf');
|
||||
|
||||
expect(savedAs).toBe('alice.conf');
|
||||
expect(blobs.map((blob) => blob.type)).toEqual(['application/octet-stream']);
|
||||
});
|
||||
});
|
||||
@@ -967,10 +967,12 @@ export class LanguageManager {
|
||||
}
|
||||
|
||||
export class FileManager {
|
||||
// Typed text/plain, Android's MediaStore saves peer.conf as peer.conf.txt;
|
||||
// octet-stream carries no extension of its own, so the given name is kept.
|
||||
static downloadTextFile(
|
||||
content: BlobPart,
|
||||
filename: string = 'file.txt',
|
||||
options: BlobPropertyBag = { type: 'text/plain' },
|
||||
options: BlobPropertyBag = { type: 'application/octet-stream' },
|
||||
): void {
|
||||
const link = window.document.createElement('a');
|
||||
link.download = filename;
|
||||
|
||||
Reference in New Issue
Block a user