layout sharing via url

[deploy]
This commit is contained in:
2023-07-08 23:19:58 +02:00
parent 3a167030da
commit 391c9d8837
7 changed files with 395 additions and 8 deletions

View File

@@ -2,7 +2,8 @@
import {serialPort, syncStatus} from "$lib/serial/connection"
import {browser} from "$app/environment"
import {page} from "$app/stores"
import {slide} from "svelte/transition"
import {slide, fly} from "svelte/transition"
import {canShare, triggerShare} from "$lib/share"
const training = [
{slug: "cpm", title: "CPM - Characters Per Minute", icon: "music_note"},
@@ -29,6 +30,10 @@
</div>
<div class="actions">
{#if $canShare}
<a transition:fly={{x: -8}} class="icon" on:click={triggerShare}>share</a>
<div transition:slide class="separator" />
{/if}
{#await import("$lib/components/PwaStatus.svelte") then { default: PwaStatus }}
<PwaStatus />
{/await}
@@ -63,7 +68,7 @@
>
cable
</a>
<a href="/" title="Statistics" class="icon account">person</a>
<a href="/stats/" title="Statistics" class="icon account">person</a>
</div>
</nav>
@@ -119,6 +124,12 @@
border-radius: 4px;
}
.separator {
width: 1px;
height: 24px;
background: var(--md-sys-color-outline-variant);
}
nav {
display: flex;
gap: 4px;
@@ -143,6 +154,10 @@
position: relative;
display: flex;
align-items: center;
justify-content: center;
aspect-ratio: 1;
padding: 4px;
@@ -168,6 +183,9 @@
}
.steps {
position: absolute;
left: 50%;
translate: -50% 0;
display: flex;
> a.icon {

View File

@@ -21,9 +21,7 @@ export async function getSharableUrl(name: string, data: any, baseHref = window.
return new Promise(async resolve => {
const reader = new FileReader()
reader.onloadend = function () {
const base64String = (reader.result as string)
.replace(/^data:application\/octet-stream;base64,/, "")
.replace(/==$/, "")
const base64String = (reader.result as string).replace(/^data:application\/octet-stream;base64,/, "")
const url = new URL(baseHref)
url.searchParams.set(name, base64String)
resolve(url)
@@ -31,3 +29,15 @@ export async function getSharableUrl(name: string, data: any, baseHref = window.
reader.readAsDataURL(await stringifyCompressed(data))
})
}
export async function parseSharableUrl<T>(
name: string,
url: string = window.location.href,
): Promise<T | undefined> {
const searchParams = new URL(url).searchParams
if (!searchParams.has(name)) return
return await fetch(`data:application/octet-stream;base64,${searchParams.get(name)}`)
.then(it => it.blob())
.then(it => parseCompressed(it))
}

22
src/lib/share.ts Normal file
View File

@@ -0,0 +1,22 @@
import type {Action} from "svelte/action"
import {readonly, writable} from "svelte/store"
const setCanShare = writable(false)
export const canShare = readonly(setCanShare)
let shareCallback: ((event: Event) => void) | undefined
export function triggerShare(event: Event) {
shareCallback?.(event)
}
export const share: Action<Window, (event: Event) => void> = (node, callback: (event: Event) => void) => {
setCanShare.set(true)
shareCallback = callback
return {
destroy() {
setCanShare.set(false)
shareCallback = undefined
},
}
}

View File

@@ -1,7 +1,35 @@
<script>
<script lang="ts">
import LayoutCC1 from "$lib/components/LayoutCC1.svelte"
import {share} from "$lib/share"
import {getSharableUrl, parseSharableUrl} from "$lib/serial/serialization"
import {layout} from "$lib/serial/connection"
import type {CharaLayout} from "$lib/serial/connection"
import tippy from "tippy.js"
import {onMount} from "svelte"
onMount(async () => {
const sharedLayout = await parseSharableUrl<CharaLayout>("layout")
if (sharedLayout) {
$layout = sharedLayout
}
})
async function shareLayout(event) {
const data = await getSharableUrl("layout", $layout)
await navigator.clipboard.writeText(data.toString())
tippy(event.target, {
content: "Share url copied!",
hideOnClick: true,
duration: 4000,
onHidden(instance) {
instance.destroy()
},
}).show()
}
</script>
<svelte:window use:share={shareLayout} />
<section>
<LayoutCC1 />
</section>

View File