Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions src/lib/client/superForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ export type FormOptions<
validators: (validators: Exclude<ValidatorsOption<T>, 'clear'>) => void;
/**
* Use a custom fetch or XMLHttpRequest implementation for this form submission. It must return an ActionResult in the response body.
* If the request is using a XMLHttprequest, the promise must be resolved when the request is complete.
* If the request is using a XMLHttprequest, the promise must be resolved when the request is complete. or the action result itself.
*/
customRequest: (
validators: (input: Parameters<SubmitFunction>[0]) => Promise<Response | XMLHttpRequest>
validators: (input: Parameters<SubmitFunction>[0]) => Promise<Response | XMLHttpRequest | ActionResult>
) => void;
}
) => MaybePromise<unknown | void>;
Expand Down Expand Up @@ -1599,7 +1599,7 @@ export function superForm<

let currentRequest: AbortController | null;
let customRequest:
| ((input: Parameters<SubmitFunction>[0]) => Promise<Response | XMLHttpRequest>)
| ((input: Parameters<SubmitFunction>[0]) => Promise<Response | XMLHttpRequest | ActionResult>)
| undefined = undefined;

const enhanced = kitEnhance(FormElement, async (submitParams) => {
Expand Down Expand Up @@ -1979,10 +1979,15 @@ export function superForm<
if (customRequest) {
if (!cancelled) _submitCancel();
const response = await customRequest(submitParams);
const result: ActionResult =
response instanceof Response
? deserialize(await response.text())
: deserialize(response.responseText);
let result: ActionResult;

if (response instanceof Response) {
result = deserialize(await response.text());
} else if (response instanceof XMLHttpRequest) {
result = deserialize(response.responseText);
} else {
result = response;
}

if (result.type === 'error') result.status = response.status;
validationResponse({ result });
Expand Down
2 changes: 1 addition & 1 deletion src/routes/(v2)/v2/customrequest/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const actions = {
const formData = await request.formData();
console.log(formData);

const form = await superValidate(formData, zod(schema), { allowFiles: true });
const form = await superValidate(formData, zod(schema));
console.log(form);

if (!form.valid) return fail(400, withFiles({ form }));
Expand Down
105 changes: 60 additions & 45 deletions src/routes/(v2)/v2/customrequest/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,52 +1,74 @@
<script lang="ts">
import { page } from '$app/stores';
import { superForm } from '$lib/index.js';
import { zod } from '$lib/adapters/index.js';
import { superForm, superValidate, message as formMessage } from '$lib/index.js';
import SuperDebug from '$lib/index.js';
import FileInput from './FileInput.svelte';
import type { PageData } from './$types.js';
import type { SubmitFunction } from '@sveltejs/kit';
import { type ActionResult, type SubmitFunction } from '@sveltejs/kit';
import { schema } from './schema.js';

export let data: PageData;
let progress = 0;

function fileUploadWithProgressbar(input: Parameters<SubmitFunction>[0]) {
return new Promise<XMLHttpRequest>((res) => {
let xhr = new XMLHttpRequest();

// listen for upload progress
xhr.upload.onprogress = function (event) {
progress = Math.round((100 * event.loaded) / event.total);
console.log(`File is ${progress}% uploaded.`);
};

// handle error
xhr.upload.onerror = function () {
console.log(`Error during the upload: ${xhr.status}.`);
};

// upload completed successfully
xhr.onload = function () {
if (xhr.readyState === xhr.DONE) {
console.log('Upload completed successfully.');
progress = 0;
res(xhr);
}
};

xhr.open('POST', input.action, true);
xhr.send(input.formData);

return xhr;
});

async function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

async function post(message: string) {
// calling an external API that does not return an action result response.
const number = Math.floor(Math.random() * 100);
if (number < 50) throw new Error('Random error');

if (number > 50 && number < 80) return { code: 'message-might-be-jackpot', message } as const;
return { code: 'message-posted', message } as const;
}

function success<T>(data: T) {
return {
type: 'success',
status: 200,
data
} as ActionResult;
}

function error<T>(status: number, data: T): ActionResult {
return {
type: 'error',
status,
error: data
};
}

async function submitMessage(input: Parameters<SubmitFunction>[0]) {
await sleep(1000);

const formData = input.formData;
console.log('posting form', formData);

const form = await superValidate(formData, zod(schema));
console.log('validated form', form);

if (!form.valid) return error(400, { form });

try {
const result = await post(form.message)

if (result.code === 'message-might-be-jackpot') {
return success(formMessage(form, 'You might be a jackpot!'));
} else {
return success(formMessage(form, `Message posted: ${result.message}`));
}

} catch (e) {
return error(400, { form });
}
}

const { form, errors, message, enhance } = superForm(data.form, {
taintedMessage: null,
onSubmit({ customRequest }) {
customRequest(fileUploadWithProgressbar);
customRequest(submitMessage);
}
});
const acceptedExtensions = '.flac, .mp3';
</script>

<SuperDebug data={$form} />
Expand All @@ -60,15 +82,8 @@
</div>
{/if}

<form method="POST" enctype="multipart/form-data" use:enhance>
<FileInput
name="track"
label="Track"
accept={acceptedExtensions}
bind:value={$form.track}
errors={$errors.track}
/>
<br /><progress max="100" value={progress}>{progress}%</progress>
<form method="POST" use:enhance>
<textarea name="message" bind:value={$form.message}></textarea>

<div><button>Submit</button></div>
</form>
Expand Down
36 changes: 0 additions & 36 deletions src/routes/(v2)/v2/customrequest/FileInput.svelte

This file was deleted.

2 changes: 1 addition & 1 deletion src/routes/(v2)/v2/customrequest/schema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { z } from 'zod';

export const schema = z.object({
track: z.instanceof(File, { message: 'Please upload a file.' })
message: z.string().min(1)
});