From ea9350a570fcb8011bb2678ad8b60486772e411d Mon Sep 17 00:00:00 2001
From: Mistivia <i@mistivia.com>
Date: Sat, 8 Feb 2025 13:55:37 +0800
Subject: impl ydke share

---
 src/components/MainPanel.svelte  | 21 +++++++++++++++++-
 src/components/RightPanel.svelte | 19 +++++++++++------
 src/control/deck.js              | 27 ++++++++++++++++++++++-
 src/control/search.js            |  7 ++++++
 src/utils.js                     | 46 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 111 insertions(+), 9 deletions(-)
 create mode 100644 src/control/search.js

diff --git a/src/components/MainPanel.svelte b/src/components/MainPanel.svelte
index b5f5f2e..5cef824 100644
--- a/src/components/MainPanel.svelte
+++ b/src/components/MainPanel.svelte
@@ -1,7 +1,12 @@
 <script lang="js">
     import CardThumb from './CardThumb.svelte';
     import { deck, setDeck } from '../control/deck';
-    import { parseYdk, genYdk, downloadStringAsFile} from '../utils'
+    import {
+        parseYdk,
+        genYdk,
+        genYdke,
+        downloadStringAsFile,
+    } from '../utils';
 
     let fileInput;
     
@@ -26,6 +31,19 @@
         downloadStringAsFile('mydeck.ydk', deckString)
     }
 
+    function shareDeck() {
+        let url = window.location.href;
+        url = url.split('#')[0]
+        url = url + '#' + genYdke($deck);
+        navigator.clipboard.writeText(url)
+            .then(() => {
+              alert('分享链接已复制到剪贴板')
+            })
+            .catch(err => {
+              alert("失败！");
+            });
+    }
+
 </script>
 
 <input bind:this={fileInput} style="display:none;" onchange={loadDeck} type="file" class="file-input" accept=".ydk" />
@@ -34,6 +52,7 @@
     <div class="control-bar">
         <button class="btn" onclick={openDeck}>打开</button>
         <button class="btn" onclick={saveDeck}>保存</button>
+        <button class="btn" onclick={shareDeck}>分享</button>
     </div>
     <div class="deck-section">
         <div class="deck-group">
diff --git a/src/components/RightPanel.svelte b/src/components/RightPanel.svelte
index 5ede942..062e33c 100644
--- a/src/components/RightPanel.svelte
+++ b/src/components/RightPanel.svelte
@@ -1,9 +1,14 @@
-<script lang="ts">
+<script lang="js">
+    import CardThumb from './CardThumb.svelte';
+    import { resultCards } from '../control/search'
 
-import CardThumb from './CardThumb.svelte';
+    function onPrevPage() {
 
-let cardList = $state([]);
+    }
 
+    function onNextPage() {
+        
+    }
 </script>
 
 <div class="right-panel">
@@ -11,9 +16,9 @@ let cardList = $state([]);
         <input type="text" placeholder="搜索卡牌...">
     </div>
     <div class="card-list">
-        {#if cardList.length > 0}
+        {#if $resultCards.length > 0}
             <div class="card-item">
-                {#each cardList as card}
+                {#each $resultCards as card}
                     <div class="card-thumbnail">
                         <CardThumb id= {card.id} />
                     </div>
@@ -23,8 +28,8 @@ let cardList = $state([]);
         {/if}
     </div>
     <div class="pagination">
-        <button class="page-btn">上一页</button>
-        <button class="page-btn">下一页</button>
+        <button class="page-btn" onclick={onPrevPage}>上一页</button>
+        <button class="page-btn" onclick={onNextPage}>下一页</button>
     </div>
 </div>
 
diff --git a/src/control/deck.js b/src/control/deck.js
index 328e106..98c48fb 100644
--- a/src/control/deck.js
+++ b/src/control/deck.js
@@ -1,11 +1,36 @@
 import { writable } from "svelte/store";
+import { parseYdke } from '../utils';
 
 let deck = writable({main: [], extra: [], side: []});
 
-let setDeck = (d) => {
+function setDeck(d) {
+    d.main.sort();
+    d.extra.sort();
+    d.side.sort();
     deck.set(d);
+    localStorage.setItem('cachedDeck', JSON.stringify(d));
 };
 
+function initDeck() {
+    let url = window.location.href.split('#');
+    if (url.length === 2) {
+        let deck = parseYdke(url[1]);
+        if (deck.main.length > 0 || deck.extra.length > 0 || deck.extra.length > 0) {
+            setDeck(deck);
+            window.location.replace("/");
+            return;
+        }
+    }
+    let cachedDeck = localStorage.getItem('cachedDeck');
+    if (cachedDeck !== null) {
+        cachedDeck = JSON.parse(cachedDeck); 
+        setDeck(cachedDeck)
+        return;
+    }
+}
+
+initDeck();
+
 export {
     deck,
     setDeck,
diff --git a/src/control/search.js b/src/control/search.js
new file mode 100644
index 0000000..93a9fc9
--- /dev/null
+++ b/src/control/search.js
@@ -0,0 +1,7 @@
+import { writable } from 'svelte/store';
+
+let resultCards = writable([]);
+
+export {
+    resultCards,
+};
diff --git a/src/utils.js b/src/utils.js
index 3176400..ba5d905 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -55,10 +55,56 @@ function downloadStringAsFile(filename, text) {
     URL.revokeObjectURL(link.href);
 }
 
+function ydkeEncode(d) {
+    let deck = [];
+    for (let card of d) {
+        let num = parseInt(card);
+        if (isNaN(num)) continue;
+        deck.push(num);
+    }
+    const buffer = new ArrayBuffer(deck.length * 4);
+    const view = new DataView(buffer);
+    for (let i = 0; i < deck.length; i++) {
+        // Little-endian
+        view.setUint32(i * 4, deck[i], true);
+    }
+    return btoa(String.fromCharCode(...new Uint8Array(buffer)));
+}
+
+function ydkeDecode(encodedDeck) {
+    const binaryString = atob(encodedDeck);
+    const buffer = new ArrayBuffer(binaryString.length);
+    const view = new DataView(buffer);
+    for (let i = 0; i < binaryString.length; i++) {
+        view.setUint8(i, binaryString.charCodeAt(i));
+    }
+    const deck = [];
+    for (let i = 0; i < buffer.byteLength; i += 4) {
+        deck.push(String(view.getUint32(i, true))); // Little-endian
+    }
+    return deck;
+}
+
+function parseYdke(ydkeString) {
+    const [mainEncoded, extraEncoded, sideEncoded] = ydkeString.split('!');
+    const main = ydkeDecode(mainEncoded);
+    const extra = ydkeDecode(extraEncoded);
+    const side = ydkeDecode(sideEncoded);
+    return { main, extra, side };
+}
+
+function genYdke(deck) {
+    const mainEncoded = ydkeEncode(deck.main);
+    const extraEncoded = ydkeEncode(deck.extra);
+    const sideEncoded = ydkeEncode(deck.side);
+    return `${mainEncoded}!${extraEncoded}!${sideEncoded}`;
+}
 
 export {
     parseYdk,
     genYdk,
+    parseYdke,
+    genYdke,
     downloadStringAsFile,
 };
 
-- 
cgit v1.0

