mirror of
https://github.com/MewoLab/AquaDX.git
synced 2026-02-07 14:37:33 +08:00
[O] LinkCard UX improvements and i18n fixes
This commit is contained in:
@@ -2,12 +2,12 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { fade, slide } from "svelte/transition"
|
||||
import type { Card, CardSummary, CardSummaryGame, ConfirmProps, AquaNetUser } from "../../libs/generalTypes";
|
||||
import { CARD, USER } from "../../libs/sdk";
|
||||
import type { Card, CardSummary, CardSummaryGame, ConfirmProps, AquaNetUser } from "../../libs/generalTypes"
|
||||
import { CARD, USER } from "../../libs/sdk"
|
||||
import moment from "moment"
|
||||
import Icon from "@iconify/svelte";
|
||||
import StatusOverlays from "../../components/StatusOverlays.svelte";
|
||||
import { t } from "../../libs/i18n";
|
||||
import Icon from "@iconify/svelte"
|
||||
import StatusOverlays from "../../components/StatusOverlays.svelte"
|
||||
import { t } from "../../libs/i18n"
|
||||
|
||||
// State
|
||||
let state: 'ready' | 'linking-AC' | 'linking-SN' | 'loading' = "loading"
|
||||
@@ -42,14 +42,22 @@
|
||||
}
|
||||
|
||||
async function doLink(id: string, migrate: string) {
|
||||
await CARD.link({cardId: id, migrate})
|
||||
await updateMe()
|
||||
try {
|
||||
await CARD.link({cardId: id, migrate})
|
||||
await updateMe()
|
||||
if (linkingType === 'AC') inputAC = ""
|
||||
if (linkingType === 'SN') inputSN = ""
|
||||
} catch (e) {
|
||||
setError(e.message, linkingType)
|
||||
}
|
||||
state = "ready"
|
||||
}
|
||||
|
||||
let linkingType: 'AC' | 'SN' = null
|
||||
async function link(type: 'AC' | 'SN') {
|
||||
if (state !== 'ready' || accountCardSummary === null) return
|
||||
state = "linking-" + type
|
||||
linkingType = type
|
||||
const id = type === 'AC' ? inputAC : inputSN
|
||||
|
||||
console.log("linking card", id)
|
||||
@@ -64,7 +72,7 @@
|
||||
// First, lookup the card summary
|
||||
const card = (await CARD.summary(id).catch(e => {
|
||||
// If card is not found, create a card and link it
|
||||
if (e.message === t('home.linkcard.notfound')) {
|
||||
if (e.message === 'Card not found') {
|
||||
doLink(id, "")
|
||||
return
|
||||
}
|
||||
@@ -156,28 +164,60 @@
|
||||
}
|
||||
}
|
||||
|
||||
function cursorPositionToCursorIndex(text: string, cursorPosition: number, effectiveCharsRegex: RegExp) {
|
||||
const textBeforeCursor = text.slice(0, cursorPosition)
|
||||
const ignoredChars = textBeforeCursor.replace(new RegExp(effectiveCharsRegex, "g"), "")
|
||||
return textBeforeCursor.length - ignoredChars.length
|
||||
}
|
||||
|
||||
function cursorIndexToCursorPosition(text: string, cursorIndex: number, effectiveCharsRegex: RegExp) {
|
||||
let i = 0
|
||||
while (i < text.length) {
|
||||
while (i < text.length && !effectiveCharsRegex.test(text[i])) i++
|
||||
if (cursorIndex === 0) break
|
||||
cursorIndex--
|
||||
i++
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// Access code input
|
||||
const inputACRegex = /^(\d{4} ){0,4}\d{0,4}$/
|
||||
let elemInputAC: HTMLInputElement
|
||||
let inputOldAC = ""
|
||||
let inputAC = ""
|
||||
let errorAC = ""
|
||||
let warningAC = ""
|
||||
|
||||
function inputACChange() {
|
||||
// Add spaces to the input
|
||||
const old = inputAC
|
||||
const cursorIndex = cursorPositionToCursorIndex(inputAC, elemInputAC.selectionStart, /\d/)
|
||||
inputAC = inputAC.replace(/\D/g, '').replace(/(.{4})/g, '$1 ').replace(/ $/, '')
|
||||
if (inputAC !== old) errorAC = ""
|
||||
const cursorPosition = cursorIndexToCursorPosition(inputAC, cursorIndex, /\d/)
|
||||
setTimeout(() => elemInputAC.selectionStart = elemInputAC.selectionEnd = cursorPosition, 0)
|
||||
if (inputAC !== inputOldAC) errorAC = ""
|
||||
warningAC = inputAC[0] === "5" ? t('home.linkcard.felica-ac-warning') : ""
|
||||
|
||||
inputOldAC = inputAC
|
||||
}
|
||||
|
||||
// Serial number input
|
||||
const inputSNRegex = /^([0-9A-Fa-f]{0,2}:){0,7}[0-9A-Fa-f]{0,2}$/
|
||||
let inputElemSN: HTMLInputElement
|
||||
let inputOldSN = ""
|
||||
let inputSN = ""
|
||||
let errorSN = ""
|
||||
|
||||
function inputSNChange() {
|
||||
// Add colons to the input
|
||||
const old = inputSN
|
||||
inputSN = inputSN.toUpperCase().replace(/[^0-9A-F]/g, '').replace(/(.{2})/g, '$1:').replace(/:$/, '')
|
||||
if (inputSN !== old) errorSN = ""
|
||||
inputSN = inputSN.toUpperCase()
|
||||
const cursorIndex = cursorPositionToCursorIndex(inputSN, inputElemSN.selectionStart, /[0-9A-F]/)
|
||||
inputSN = inputSN.replace(/[^0-9A-F]/g, '').replace(/(.{2})/g, '$1:').replace(/:$/, '')
|
||||
const cursorPosition = cursorIndexToCursorPosition(inputSN, cursorIndex, /[0-9A-F]/)
|
||||
setTimeout(() => inputElemSN.selectionStart = inputElemSN.selectionEnd = cursorPosition, 0)
|
||||
if (inputSN !== inputOldSN) errorSN = ""
|
||||
|
||||
inputOldSN = inputSN
|
||||
}
|
||||
|
||||
function formatLUID(luid: string, ghost: boolean = false) {
|
||||
@@ -253,7 +293,8 @@
|
||||
<p>{t('home.linkcard.access-code')}</p>
|
||||
<label>
|
||||
<!-- DO NOT change the order of bind:value and on:input. Their order determines the order of reactivity -->
|
||||
<input placeholder="e.g. 5200 1234 5678 9012 3456"
|
||||
<input bind:this={elemInputAC}
|
||||
placeholder="e.g. 2408 1234 5678 9012 3456 / 0008 1234 5678 8765 4321"
|
||||
on:keydown={(e) => {
|
||||
e.key === "Enter" && link('AC')
|
||||
// Ensure key is numeric
|
||||
@@ -261,13 +302,22 @@
|
||||
}}
|
||||
bind:value={inputAC}
|
||||
on:input={inputACChange}
|
||||
class:error={inputAC && (!inputACRegex.test(inputAC) || errorAC)}>
|
||||
class:error={inputAC && (!inputACRegex.test(inputAC) || errorAC)}
|
||||
class:warning={inputAC && warningAC}>
|
||||
{#if inputAC.length > 0}
|
||||
<button transition:slide={{axis: 'x'}} on:click={() => {link('AC');inputAC=''}}>{t('home.linkcard.link')}</button>
|
||||
<button transition:slide={{axis: 'x'}} on:click={() => link('AC')}>{t('home.linkcard.link')}</button>
|
||||
{/if}
|
||||
</label>
|
||||
{#if errorAC}
|
||||
<p class="error" transition:slide>{errorAC}</p>
|
||||
<p class="error" style={warningAC ? "margin-bottom: 0" : ""} transition:slide>{errorAC}</p>
|
||||
{/if}
|
||||
{#if warningAC}
|
||||
<!-- Transition temporarily adds `overflow: hidden` which leads to BFC issue, breaking margin collapse -->
|
||||
<div style="overflow: hidden" transition:slide>
|
||||
{#each warningAC.trim().split("\n") as paragraph}
|
||||
<p class="warning">{paragraph}</p>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -280,7 +330,8 @@
|
||||
{t('home.linkcard.enter-sn2')}
|
||||
</p>
|
||||
<label>
|
||||
<input placeholder="e.g. 01:2E:1A:2B:3C:4D:5E:6F"
|
||||
<input bind:this={inputElemSN}
|
||||
placeholder="e.g. 01:2E:1A:2B:3C:4D:5E:6F"
|
||||
on:keydown={(e) => {
|
||||
e.key === "Enter" && link('SN')
|
||||
// Ensure key is hex or colon
|
||||
@@ -290,7 +341,7 @@
|
||||
on:input={inputSNChange}
|
||||
class:error={inputSN && (!inputSNRegex.test(inputSN) || errorSN)}>
|
||||
{#if inputSN.length > 0}
|
||||
<button transition:slide={{axis: 'x'}} on:click={() => {link('SN'); inputSN = ''}}>{t('home.linkcard.link')}</button>
|
||||
<button transition:slide={{axis: 'x'}} on:click={() => link('SN')}>{t('home.linkcard.link')}</button>
|
||||
{/if}
|
||||
</label>
|
||||
{#if errorSN}
|
||||
|
||||
Reference in New Issue
Block a user