159 lines
6.3 KiB
TypeScript
159 lines
6.3 KiB
TypeScript
import React from "react";
|
|
import dayjs from "dayjs";
|
|
import { FlexWidget, OverlapWidget, SvgWidget, TextWidget } from "react-native-android-widget";
|
|
import { AS } from "../../storageControl";
|
|
import { STORAGE_KEYS } from "../../constants";
|
|
import { WidgetColors, widgetLightColors } from "./widget-theme";
|
|
|
|
type LastFelicaSnapshot = {
|
|
balance: number;
|
|
idm: string;
|
|
systemCode?: string;
|
|
scannedAt: string;
|
|
};
|
|
|
|
export async function getFelicaQuickAccessData() {
|
|
const nowText = dayjs().format("HH:mm");
|
|
const snapshot = (await AS.getItem(STORAGE_KEYS.FELICA_LAST_SNAPSHOT).catch(
|
|
() => null
|
|
)) as LastFelicaSnapshot | null;
|
|
|
|
const hasBalance =
|
|
snapshot != null && typeof snapshot.balance === "number" && snapshot.balance >= 0;
|
|
|
|
return {
|
|
nowText,
|
|
amountText: hasBalance ? `\u00A5${snapshot.balance.toLocaleString()}` : "未読取",
|
|
detailText:
|
|
snapshot?.scannedAt != null
|
|
? `最終読取: ${snapshot.scannedAt}`
|
|
: "カードをタップして読取開始",
|
|
};
|
|
}
|
|
|
|
// IC card + NFC arcs, -22° rotated, as widget background
|
|
const IC_CARD_BG_SVG_LIGHT = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 160">
|
|
<g transform="translate(168,-32) rotate(-22)" opacity="0.24">
|
|
<rect x="0" y="0" width="208" height="132" rx="14" fill="#0099CC"/>
|
|
<rect x="0" y="28" width="208" height="32" fill="#007AAA"/>
|
|
<rect x="16" y="74" width="38" height="26" rx="4" fill="#FFD966"/>
|
|
<line x1="16" y1="87" x2="54" y2="87" stroke="#C8A800" stroke-width="1.5"/>
|
|
<line x1="29" y1="74" x2="29" y2="100" stroke="#C8A800" stroke-width="1.5"/>
|
|
<line x1="40" y1="74" x2="40" y2="100" stroke="#C8A800" stroke-width="1.5"/>
|
|
<circle cx="68" cy="88" r="5" fill="white" opacity="0.55"/>
|
|
<circle cx="82" cy="88" r="5" fill="white" opacity="0.55"/>
|
|
<circle cx="96" cy="88" r="5" fill="white" opacity="0.55"/>
|
|
<circle cx="110" cy="88" r="5" fill="white" opacity="0.55"/>
|
|
<circle cx="154" cy="80" r="7" fill="white" opacity="0.65"/>
|
|
<path d="M167 63 A20 20 0 0 1 167 97" fill="none" stroke="white" stroke-width="5" stroke-linecap="round" opacity="0.68"/>
|
|
<path d="M178 52 A34 34 0 0 1 178 108" fill="none" stroke="white" stroke-width="5" stroke-linecap="round" opacity="0.50"/>
|
|
<path d="M189 41 A48 48 0 0 1 189 119" fill="none" stroke="white" stroke-width="5" stroke-linecap="round" opacity="0.34"/>
|
|
</g>
|
|
<g transform="translate(4,80) rotate(-22)" stroke="#0A88CC" fill="none" stroke-linecap="round">
|
|
<circle cx="16" cy="42" r="5" fill="#0A88CC" stroke="none" opacity="0.23"/>
|
|
<path d="M28 30 A17 17 0 0 1 28 54" stroke-width="5" opacity="0.20"/>
|
|
<path d="M38 22 A27 27 0 0 1 38 62" stroke-width="5" opacity="0.15"/>
|
|
<path d="M48 14 A37 37 0 0 1 48 70" stroke-width="5" opacity="0.10"/>
|
|
</g>
|
|
</svg>`;
|
|
|
|
const IC_CARD_BG_SVG_DARK = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 160">
|
|
<g transform="translate(168,-32) rotate(-22)" opacity="0.30">
|
|
<rect x="0" y="0" width="208" height="132" rx="14" fill="#006688"/>
|
|
<rect x="0" y="28" width="208" height="32" fill="#005566"/>
|
|
<rect x="16" y="74" width="38" height="26" rx="4" fill="#AA8833"/>
|
|
<line x1="16" y1="87" x2="54" y2="87" stroke="#887722" stroke-width="1.5"/>
|
|
<line x1="29" y1="74" x2="29" y2="100" stroke="#887722" stroke-width="1.5"/>
|
|
<line x1="40" y1="74" x2="40" y2="100" stroke="#887722" stroke-width="1.5"/>
|
|
<circle cx="68" cy="88" r="5" fill="#8ecfff" opacity="0.35"/>
|
|
<circle cx="82" cy="88" r="5" fill="#8ecfff" opacity="0.35"/>
|
|
<circle cx="96" cy="88" r="5" fill="#8ecfff" opacity="0.35"/>
|
|
<circle cx="110" cy="88" r="5" fill="#8ecfff" opacity="0.35"/>
|
|
<circle cx="154" cy="80" r="7" fill="#8ecfff" opacity="0.40"/>
|
|
<path d="M167 63 A20 20 0 0 1 167 97" fill="none" stroke="#8ecfff" stroke-width="5" stroke-linecap="round" opacity="0.45"/>
|
|
<path d="M178 52 A34 34 0 0 1 178 108" fill="none" stroke="#8ecfff" stroke-width="5" stroke-linecap="round" opacity="0.30"/>
|
|
<path d="M189 41 A48 48 0 0 1 189 119" fill="none" stroke="#8ecfff" stroke-width="5" stroke-linecap="round" opacity="0.18"/>
|
|
</g>
|
|
<g transform="translate(4,80) rotate(-22)" stroke="#337799" fill="none" stroke-linecap="round">
|
|
<circle cx="16" cy="42" r="5" fill="#337799" stroke="none" opacity="0.23"/>
|
|
<path d="M28 30 A17 17 0 0 1 28 54" stroke-width="5" opacity="0.20"/>
|
|
<path d="M38 22 A27 27 0 0 1 38 62" stroke-width="5" opacity="0.15"/>
|
|
<path d="M48 14 A37 37 0 0 1 48 70" stroke-width="5" opacity="0.10"/>
|
|
</g>
|
|
</svg>`;
|
|
|
|
export function FelicaQuickAccessWidget({
|
|
amountText,
|
|
detailText,
|
|
colors = widgetLightColors,
|
|
}: {
|
|
amountText: string;
|
|
nowText: string;
|
|
detailText: string;
|
|
colors?: WidgetColors;
|
|
}) {
|
|
const hasValue = amountText !== "未読取";
|
|
const isDark = colors.background !== "#ffffff" && colors.background !== "#DDF2FF";
|
|
|
|
return (
|
|
<OverlapWidget
|
|
style={{
|
|
height: "match_parent",
|
|
width: "match_parent",
|
|
backgroundColor: colors.felicaBg,
|
|
borderRadius: 20,
|
|
overflow: "hidden",
|
|
}}
|
|
clickAction="OPEN_URI"
|
|
clickActionData={{ uri: "jrshikoku://open/felica" }}
|
|
>
|
|
{/* Background: IC card + NFC icons, rotated */}
|
|
<SvgWidget
|
|
style={{ height: "match_parent", width: "match_parent" }}
|
|
svg={isDark ? IC_CARD_BG_SVG_DARK : IC_CARD_BG_SVG_LIGHT}
|
|
/>
|
|
{/* Foreground: balance only */}
|
|
<FlexWidget
|
|
style={{
|
|
height: "match_parent",
|
|
width: "match_parent",
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
paddingLeft: 16,
|
|
paddingRight: 16,
|
|
}}
|
|
>
|
|
<TextWidget
|
|
text={amountText}
|
|
style={{
|
|
fontSize: 48,
|
|
fontWeight: "bold",
|
|
color: hasValue ? colors.felicaText : colors.felicaTextMuted,
|
|
}}
|
|
/>
|
|
</FlexWidget>
|
|
{/* Bottom-right: scan timestamp */}
|
|
{hasValue && (
|
|
<FlexWidget
|
|
style={{
|
|
height: "match_parent",
|
|
width: "match_parent",
|
|
justifyContent: "flex-end",
|
|
alignItems: "flex-end",
|
|
paddingRight: 10,
|
|
paddingBottom: 8,
|
|
}}
|
|
>
|
|
<TextWidget
|
|
text={detailText}
|
|
style={{
|
|
fontSize: 11,
|
|
color: colors.felicaDetail,
|
|
}}
|
|
/>
|
|
</FlexWidget>
|
|
)}
|
|
</OverlapWidget>
|
|
);
|
|
}
|