mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-01-07 02:22:52 +00:00
layout sharing via url
[deploy]
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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
22
src/lib/share.ts
Normal 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
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
0
src/routes/stats/+page.svelte
Normal file
0
src/routes/stats/+page.svelte
Normal file
Reference in New Issue
Block a user