Skip to content

Commit

Permalink
qr redesign
Browse files Browse the repository at this point in the history
  • Loading branch information
futurepaul committed Jun 30, 2023
1 parent 534a1ba commit cb9e6de
Show file tree
Hide file tree
Showing 11 changed files with 257 additions and 47 deletions.
3 changes: 3 additions & 0 deletions src/assets/icons/copy-black.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/icons/share-black.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/assets/icons/side-to-side.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 17 additions & 3 deletions src/components/Amount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export function Amount(props: {
loading?: boolean;
centered?: boolean;
icon?: "lightning" | "chain";
whiteBg?: boolean;
}) {
const [state, _] = useMegaStore();

Expand All @@ -26,7 +27,9 @@ export function Amount(props: {
return (
<div
class="flex flex-col gap-1"
classList={{ "items-center": props.centered }}
classList={{
"items-center": props.centered
}}
>
<div class="flex gap-2 items-center">
<Show when={props.icon === "lightning"}>
Expand All @@ -35,7 +38,12 @@ export function Amount(props: {
<Show when={props.icon === "chain"}>
<img src={chain} alt="chain" class="h-[18px]" />
</Show>
<h1 class="text-2xl font-light">
<h1
class="text-2xl font-light"
classList={{
"text-black": props.whiteBg
}}
>
{props.loading
? "..."
: prettyPrintAmount(props.amountSats)}
Expand All @@ -44,7 +52,13 @@ export function Amount(props: {
</h1>
</div>
<Show when={props.showFiat}>
<h2 class="text-sm font-light text-white/70">
<h2
class="text-sm font-light"
classList={{
"text-black": props.whiteBg,
"text-white/70": !props.whiteBg
}}
>
&#8776; {props.loading ? "..." : amountInUsd()}&nbsp;
<span class="text-sm">USD</span>
</h2>
Expand Down
111 changes: 111 additions & 0 deletions src/components/IntegratedQR.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { Match, Show, Switch } from "solid-js";
import { QRCodeSVG } from "solid-qr-code";
import { ReceiveFlavor } from "~/routes/Receive";
import { useCopy } from "~/utils/useCopy";
import { Amount } from "./Amount";
import { TruncateMiddle } from "./ShareCard";
import copyBlack from "~/assets/icons/copy-black.svg";
import shareBlack from "~/assets/icons/share-black.svg";
import chainBlack from "~/assets/icons/chain-black.svg";
import boltBlack from "~/assets/icons/bolt-black.svg";

function KindIndicator(props: { kind: ReceiveFlavor }) {
return (
<div class="text-black flex flex-col items-end">
<Switch>
<Match when={props.kind === "onchain"}>
<h3 class="font-semibold">On-chain</h3>
<img src={chainBlack} alt="chain" />
</Match>

<Match when={props.kind === "lightning"}>
<h3 class="font-semibold">Lightning</h3>
<img src={boltBlack} alt="bolt" />
</Match>

<Match when={props.kind === "unified"}>
<h3 class="font-semibold">Unified</h3>
<div class="flex gap-1">
<img src={chainBlack} alt="chain" />
<img src={boltBlack} alt="bolt" />
</div>
</Match>
</Switch>
</div>
);
}

async function share(receiveString: string) {
// If the browser doesn't support share we can just copy the address
if (!navigator.share) {
console.error("Share not supported");
}
const shareData: ShareData = {
title: "Mutiny Wallet",
text: receiveString
};
try {
await navigator.share(shareData);
} catch (e) {
console.error(e);
}
}

export function IntegratedQr(props: {
value: string;
amountSats: string;
kind: ReceiveFlavor;
}) {
const [copy, copied] = useCopy({ copiedTimeout: 1000 });
return (
<div
id="qr"
class="w-full bg-white rounded-xl relative flex flex-col items-center px-4"
onClick={() => copy(props.value)}
>
<Show when={copied()}>
<div class="absolute w-full h-full bg-neutral-900/60 z-50 rounded-xl flex flex-col items-center justify-center transition-all">
<p class="text-xl font-bold">Copied</p>
</div>
</Show>
<div class="w-full flex justify-between items-center py-4 max-w-[256px]">
<Amount
amountSats={Number(props.amountSats)}
showFiat
whiteBg
/>
<KindIndicator kind={props.kind} />
</div>

<QRCodeSVG
value={props.value}
class="w-full h-full max-h-[256px]"
/>
<div
class="w-full grid gap-1 py-4 max-w-[256px] "
classList={{
"grid-cols-[2rem_minmax(0,1fr)_2rem]": !!navigator.share,
"grid-cols-[minmax(0,1fr)_2rem]": !navigator.share
}}
>
<Show when={!!navigator.share}>
<button
class="justify-self-start"
onClick={(_) => share(props.value)}
>
<img src={shareBlack} alt="share" />
</button>
</Show>
<div class="">
<TruncateMiddle text={props.value} whiteBg />
</div>
<button
class=" justify-self-end"
onClick={() => copy(props.value)}
>
<img src={copyBlack} alt="copy" />
</button>
</div>
</div>
);
}
27 changes: 21 additions & 6 deletions src/components/ShareCard.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { Card, VStack } from "~/components/layout";
import { useCopy } from "~/utils/useCopy";
import copyIcon from "~/assets/icons/copy.svg";
import copyBlack from "~/assets/icons/copy-black.svg";
import shareIcon from "~/assets/icons/share.svg";
import shareBlack from "~/assets/icons/share-black.svg";
import eyeIcon from "~/assets/icons/eye.svg";
import { Show, createSignal } from "solid-js";
import { JsonModal } from "./JsonModal";

const STYLE =
"px-4 py-2 rounded-xl border-2 border-white flex gap-2 items-center font-semibold hover:text-m-blue transition-colors";

export function ShareButton(props: { receiveString: string }) {
export function ShareButton(props: {
receiveString: string;
whiteBg?: boolean;
}) {
async function share(receiveString: string) {
// If the browser doesn't support share we can just copy the address
if (!navigator.share) {
Expand All @@ -29,14 +34,20 @@ export function ShareButton(props: { receiveString: string }) {
return (
<button class={STYLE} onClick={(_) => share(props.receiveString)}>
<span>Share</span>
<img src={shareIcon} alt="share" />
<img src={props.whiteBg ? shareBlack : shareIcon} alt="share" />
</button>
);
}

export function TruncateMiddle(props: { text: string }) {
export function TruncateMiddle(props: { text: string; whiteBg?: boolean }) {
return (
<div class="flex text-neutral-300 font-mono">
<div
class="flex font-mono"
classList={{
"text-black": props.whiteBg,
"text-neutral-300": !props.whiteBg
}}
>
<span class="truncate">{props.text}</span>
<span class="pr-2">
{props.text.length > 8 ? props.text.slice(-8) : ""}
Expand Down Expand Up @@ -65,7 +76,11 @@ export function StringShower(props: { text: string }) {
);
}

export function CopyButton(props: { text?: string; title?: string }) {
export function CopyButton(props: {
text?: string;
title?: string;
whiteBg?: boolean;
}) {
const [copy, copied] = useCopy({ copiedTimeout: 1000 });

function handleCopy() {
Expand All @@ -75,7 +90,7 @@ export function CopyButton(props: { text?: string; title?: string }) {
return (
<button class={STYLE} onClick={handleCopy}>
{copied() ? "Copied" : props.title ?? "Copy"}
<img src={copyIcon} alt="copy" />
<img src={props.whiteBg ? copyBlack : copyIcon} alt="copy" />
</button>
);
}
Expand Down
10 changes: 7 additions & 3 deletions src/components/layout/Radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@ export function StyledRadioGroup(props: {
onValueChange: (value: string) => void;
small?: boolean;
accent?: "red" | "white";
vertical?: boolean;
}) {
return (
// TODO: rewrite this with CVA, props are bad for tailwind
<RadioGroup.Root
value={props.value}
onChange={props.onValueChange}
class={"grid w-full gap-4"}
class={"w-full gap-4"}
classList={{
"grid-cols-2": props.choices.length === 2,
"grid-cols-3": props.choices.length === 3,
"flex flex-col": props.vertical,
"grid grid-cols-2":
props.choices.length === 2 && !props.vertical,
"grid grid-cols-3":
props.choices.length === 3 && !props.vertical,
"gap-2": props.small
}}
>
Expand Down
40 changes: 37 additions & 3 deletions src/components/layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from "solid-js";
import Linkify from "./Linkify";
import { Button, ButtonLink } from "./Button";
import { Checkbox as KCheckbox, Separator } from "@kobalte/core";
import { Dialog, Checkbox as KCheckbox, Separator } from "@kobalte/core";
import { useMegaStore } from "~/state/megaStore";
import check from "~/assets/icons/check.svg";
import { MutinyTagItem } from "~/utils/tags";
Expand Down Expand Up @@ -75,7 +75,6 @@ export const SettingsCard: ParentComponent<{
);
};


export const SafeArea: ParentComponent = (props) => {
return (
<div class="h-[100dvh] safe-left safe-right">
Expand Down Expand Up @@ -166,7 +165,7 @@ export const LargeHeader: ParentComponent<{ action?: JSX.Element }> = (
) => {
return (
<header class="w-full flex justify-between items-center mt-4 mb-2">
<h1 class="text-3xl font-semibold">{props.children}</h1>
<h1 class="text-2xl font-semibold">{props.children}</h1>
<Show when={props.action}>{props.action}</Show>
</header>
);
Expand Down Expand Up @@ -282,3 +281,38 @@ export function ModalCloseButton() {
</button>
);
}

export const SIMPLE_OVERLAY = "fixed inset-0 z-50 bg-black/70 backdrop-blur-md";
export const SIMPLE_DIALOG_POSITIONER =
"fixed inset-0 z-50 flex items-center justify-center";
export const SIMPLE_DIALOG_CONTENT =
"max-w-[500px] w-[90vw] max-h-[100dvh] overflow-y-scroll disable-scrollbars mx-4 p-4 bg-neutral-800/80 backdrop-blur-md shadow-xl rounded-xl border border-white/10";

export const SimpleDialog: ParentComponent<{
title: string;
open: boolean;
setOpen: (open: boolean) => void;
}> = (props) => {
return (
<Dialog.Root open={props.open} onOpenChange={props.setOpen}>
<Dialog.Portal>
<Dialog.Overlay class={SIMPLE_OVERLAY} />
<div class={SIMPLE_DIALOG_POSITIONER}>
<Dialog.Content class={SIMPLE_DIALOG_CONTENT}>
<div class="flex justify-between mb-2 items-center">
<Dialog.Title>
<SmallHeader>{props.title}</SmallHeader>
</Dialog.Title>
<Dialog.CloseButton>
<ModalCloseButton />
</Dialog.CloseButton>
</div>
<Dialog.Description class="flex flex-col gap-4">
{props.children}
</Dialog.Description>
</Dialog.Content>
</div>
</Dialog.Portal>
</Dialog.Root>
);
};
Loading

0 comments on commit cb9e6de

Please sign in to comment.