Skip to content

Commit

Permalink
feat: report link sharing (#1397)
Browse files Browse the repository at this point in the history
  • Loading branch information
ryon committed Apr 28, 2023
1 parent 63bf659 commit a1eead0
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 20 deletions.
17 changes: 15 additions & 2 deletions web/client/src/lib/Button.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@
import ButtonContent from './ButtonContent.svelte';
/** @type {string} */
export let className = '';
let className = '';
export { className as class };
/** @type {boolean|null} */
export let disabled = null;
/** @type {string|null} */
export let href = null;
/** @type {'sm'|'md'} */
export let size = 'md';
/** @type {string|null} */
export let target = null;
Expand All @@ -22,11 +26,11 @@
$: buttonClass = [
'btn',
`btn-${variant}`,
`btn-${size}`,
`focus-ring`,
disabled ? 'cursor-not-allowed opacity-60' : '',
'inline-block',
'font-mono',
'px-8 py-2',
'rounded',
'shadow',
'transition-transform duration-100 [&:not(:disabled)]:active:scale-95',
Expand Down Expand Up @@ -69,4 +73,13 @@
@apply bg-none border-0 shadow-none text-mobi-purple-safe;
@apply px-4;
}
.btn-sm {
@apply text-sm;
@apply px-3 py-1;
}
.btn-md {
@apply px-8 py-2;
}
</style>
80 changes: 80 additions & 0 deletions web/client/src/lib/CopyButton.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<script>
import Button from './Button.svelte';
import { fly } from 'svelte/transition';
/** @type {string} */
let className = '';
export { className as class };
/** @type {string} */
export let content = '';
/** @type {string?} */
export let href = null;
/** @type {string?} */
export let ready = null;
/** @type {'sm'|'md'} */
export let size = 'md';
/** @type {'ready'|'waiting'|'error'|'done'} */
let status = 'ready';
/** @param {MouseEvent} event */
async function handleClick(event) {
event.preventDefault();
status = 'waiting';
try {
// attempt to write to clipboard
await navigator.clipboard.writeText(content);
status = 'done';
} catch (e) {
console.error(e);
status = 'error';
} finally {
resetIcon();
}
}
/** @param {number} delay ms */
function resetIcon(delay = 3500) {
setTimeout(() => (status = 'ready'), delay);
}
</script>

<Button bind:class={className} {href} {size} on:click={handleClick}>
<div class="inline-grid overflow-hidden">
{#key status}
<div
in:fly|local={{ y: 20 }}
out:fly|local={{ y: -20 }}
style="grid-area: 1 / 1 / 1 / 1"
>
{#if ['ready', 'waiting'].includes(status)}
{#if ready}
{ready}
{:else}
<slot />
{/if}

{#if status == 'waiting'}
<i
class="fa-solid fa-circle-notch fa-spin"
style="--fa-animation-duration: 500ms"
/>
{:else}
<i class="fa-regular fa-sm fa-clone" data-content={content} />
{/if}
{:else if status == 'done'}
Copied
<i class="fa-solid fa-check" />
{:else}
Error
<i class="fa-solid fa-ban" />
{/if}
</div>
{/key}
</div>
</Button>
59 changes: 47 additions & 12 deletions web/client/src/lib/StatusModal.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<script>
import { backOut, circInOut, elasticOut } from 'svelte/easing';
import { backOut, backIn, circInOut, elasticOut } from 'svelte/easing';
import { fade, fly, scale, slide } from 'svelte/transition';
import Button from './Button.svelte';
import CopyButton from './CopyButton.svelte';
/** @type {?HTMLDialogElement} */
export let dialog = null;
Expand All @@ -14,16 +15,11 @@
export let reportUrl;
$: isReady = status === 'ready';
function handleDismiss() {
console.log('handle dismiss');
if (dialog) {
dialog.close();
}
}
$: reportUrlJson = reportUrl.replace(/.html$/, '.json');
/** @type {Object.<string, string>} */
const statusMessages = {
error: 'Report not found',
authorizing: 'Preparing upload\u2026',
uploading: 'Uploading file\u2026',
processing: 'Validating GTFS data\u2026',
Expand All @@ -33,6 +29,7 @@

<dialog
bind:this={dialog}
on:close
class="backdrop:bg-mobi-light-blue/20 backdrop:backdrop-blur-sm
rounded-lg shadow-2xl shadow-indigo-800/50
overflow-hidden
Expand All @@ -41,10 +38,10 @@
transition-all
"
>
{#if status == 'ready'}
{#if ['error', 'ready'].includes(status)}
<form method="dialog" class="flex justify-end -mt-7 -mr-7">
<span in:scale|local>
<Button type="submit" className="px-4" variant="link">
<Button type="submit" class="!px-4" variant="link">
<i class="fa-solid fa-times" />
<div class="sr-only">Dismiss</div>
</Button>
Expand Down Expand Up @@ -77,6 +74,8 @@
class="fa-solid fa-2x fa-check-circle text-mobi-light-blue"
/>
{/key}
{:else if status == 'error'}
<i class="fa-regular fa-2x fa-face-frown text-mobi-purple" />
{/if}
</div>
{/key}
Expand All @@ -100,15 +99,51 @@
</div>
{/if}

{#if status == 'error'}
<div
in:slide|local={{ easing: backOut }}
out:slide|local={{ easing: backIn }}
>
<div class="text-black/50">Has it been more than 30&nbsp;days?</div>

<form method="dialog" class="flex flex-col items-stretch mt-8">
<Button type="submit">Start Over</Button>
</form>
</div>
{/if}

{#if isReady && reportUrl}
<div
transition:slide|local={{ easing: backOut }}
class="mt-8 flex flex-col"
in:slide|local={{ easing: backOut }}
out:slide|local={{ easing: backIn }}
class="mt-8 flex flex-col gap-2"
>
<Button href={reportUrl} target="_blank">
Open Report
<i class="fa-solid fa-xs fa-arrow-up-right-from-square" />
</Button>

<hr class="my-4 " />

<p class="mb-4 text-sm text-black/50">
You may copy and share the links below. Your report will be available
for at least 30&nbsp;days.
</p>

<div class="flex items-baseline gap-2 text-sm">
<CopyButton
href={reportUrl}
class="flex-1"
size="sm"
content={reportUrl}>HTML</CopyButton
>
<CopyButton
href={reportUrlJson}
class="flex-1"
size="sm"
content={reportUrlJson}>JSON</CopyButton
>
</div>
</div>
{/if}
</dialog>
2 changes: 1 addition & 1 deletion web/client/src/lib/forms/FileField.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
/>
</div>
<Button type="button" className="py-1" on:click={clickFileInput}>
<Button type="button" class="py-1" on:click={clickFileInput}>
{buttonText}
</Button>
</LabeledField>
39 changes: 34 additions & 5 deletions web/client/src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import countryCodes from '$lib/countryCodes';
import { fly } from 'svelte/transition';
import { onMount } from 'svelte';
import { onMount, tick } from 'svelte';
import { quintOut } from 'svelte/easing';
/**
Expand All @@ -24,7 +24,8 @@
*/
/**
* @typedef { 'authorizing'
* @typedef { 'error'
* | 'authorizing'
* | 'uploading'
* | 'processing'
* | 'ready'
Expand Down Expand Up @@ -130,7 +131,6 @@
* @param {boolean} shouldUpload
**/
function handleFile(file, shouldUpload = false) {
console.log('handling file', file.name);
clearFile();
if (file.type != 'application/zip') {
Expand All @@ -147,7 +147,6 @@
/** @param {FileList} files */
function handleFiles(files, shouldUpload = false) {
console.log('handling files', files);
clearFile();
if (files.length > 1) {
addError('Sorry, you must upload only a single ZIP file.');
Expand All @@ -156,6 +155,22 @@
}
}
async function handleUrlParams() {
// if there's a report id in the url like ?report=[id]
// let's try to show the report info in a StatusModal
const params = new URLSearchParams(window.location.search);
const reportId = params.get('report');
if (reportId) {
jobId = reportId;
await tick();
const exists = await reportExists().catch(() => false);
updateStatus(exists ? 'ready' : 'error');
} else {
// if there's no id, clear status modal
statusModal.close();
}
}
/** @param {Event} event */
function handleReset(event) {
event.preventDefault();
Expand Down Expand Up @@ -299,6 +314,9 @@
if (canContinue) {
jobId = job.jobId;
// push a url with the ID so it can be referred to later (see handleUrlParams)
history.pushState(null, '', `?report=${jobId}`);
// upload the file (if applicable)
if (source instanceof File) {
await putFile(job.url, source).catch((error) => {
Expand Down Expand Up @@ -336,7 +354,11 @@
form = /** @type {HTMLFormElement} */ (
document.getElementById('validator-form')
);
detectRegion();
handleUrlParams();
addEventListener('popstate', handleUrlParams);
});
</script>
Expand Down Expand Up @@ -419,4 +441,11 @@
</DropTarget>
</div>
<StatusModal bind:dialog={statusModal} bind:status {reportUrl} />
<StatusModal
bind:dialog={statusModal}
bind:status
{reportUrl}
on:close={() => {
status = 'processing';
}}
/>

0 comments on commit a1eead0

Please sign in to comment.