Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flow-from-layout #3

Merged
merged 5 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[Architecture](./architecture.md)

[Selector API](./selector-api.md)

[Option click behavior](./option-click-behavior.md)
24 changes: 24 additions & 0 deletions docs/option-click-behavior.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Wallet provider option click behavior

The click behavior of a provider option depends on whether the provider is installed as well as whether there's enough space in the UI to show a side panel.

When an option contains the `installPrompt` property, the provider it represents is considered to not be installed. When absent, the provider is considered to be installed.

The side panel is an informational area of the selector which is shown when there's enough space for it in the UI.

The user flow when clicking uninstalled providers is a two step process if there's enough space for the side panel,

1. Clicking the provider option opens the side panel.
2. Clicking the side panel action takes the user to the app store.

and a one step process if there is no space for the panel,

1. Clicking the provider option takes the user to the app store.

A special case is when all provider options represent uninstalled providers: no side panel is ever shown, regardless of available UI space, and clicking the provider option takes the user to the app store.

Clicking on an installed provider returns the selection to the caller.

## Technical details

An `isShowingSidePanel()` helper is used to check whether there's a side panel in the DOM and its display style. This function is how UI layout requirements are tied to the option click behavior.
50 changes: 18 additions & 32 deletions src/exampleApp/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,74 +2,59 @@ import { Config } from "../lib";

export function mockNoneInstalled(): Config {
return {
providers: [
options: [
{
name: "Wallet 1",
id: "wallet-1",
icon: "https://picsum.photos/101",
isInstalled: false,
chromeWebStoreUrl: "https://example.com/?chrome-store-wallet-1",
googlePlayStoreUrl: "https://example.com/?google-play-wallet-1",
iOSAppStoreUrl: "https://example.com/?ios-wallet-1",
installPrompt: {
url: "https://example.com/?chrome-store-wallet-1",
},
},
{
name: "Wallet 2",
id: "wallet-2",
icon: "https://picsum.photos/102",
isInstalled: false,
chromeWebStoreUrl: "https://example.com/?chrome-store-wallet-2",
googlePlayStoreUrl: "https://example.com/?google-play-wallet-2",
iOSAppStoreUrl: "https://example.com/?ios-wallet-2",
installPrompt: {
url: "https://example.com/?chrome-store-wallet-2",
},
},
],
};
}

export function mockSomeUninstalled(): Config {
return {
providers: [
options: [
{
name: "Wallet 1",
id: "wallet-1",
icon: "https://picsum.photos/101",
isInstalled: true,
chromeWebStoreUrl: "https://example.com/?chrome-store-wallet-1",
googlePlayStoreUrl: "https://example.com/?google-play-wallet-1",
iOSAppStoreUrl: "https://example.com/?ios-wallet-1",
},
{
name: "Wallet 2",
id: "wallet-2",
icon: "https://picsum.photos/102",
isInstalled: false,
chromeWebStoreUrl: "https://example.com/?chrome-store-wallet-2",
googlePlayStoreUrl: "https://example.com/?google-play-wallet-2",
iOSAppStoreUrl: "https://example.com/?ios-wallet-2",
installPrompt: {
url: "https://example.com/?chrome-store-wallet-2",
},
},
],
};
}

export function mockAllInstalled(): Config {
return {
providers: [
options: [
{
name: "Wallet 1",
id: "wallet-1",
icon: "https://picsum.photos/101",
isInstalled: true,
chromeWebStoreUrl: "https://example.com/?chrome-store-wallet-1",
googlePlayStoreUrl: "https://example.com/?google-play-wallet-1",
iOSAppStoreUrl: "https://example.com/?ios-wallet-1",
},
{
name: "Wallet 2",
id: "wallet-2",
icon: "https://picsum.photos/102",
isInstalled: true,
chromeWebStoreUrl: "https://example.com/?chrome-store-wallet-2",
googlePlayStoreUrl: "https://example.com/?google-play-wallet-2",
iOSAppStoreUrl: "https://example.com/?ios-wallet-2",
},
],
};
Expand All @@ -78,16 +63,17 @@ export function mockAllInstalled(): Config {
export function mockManySomeInstalled() {
return {
// 25 wallets
providers: Array.from({ length: 50 }, (_, i) => {
options: Array.from({ length: 50 }, (_, i) => {
const id = `wallet-${i + 1}`;
return {
name: `Wallet ${i + 1}`,
id,
icon: `https://picsum.photos/${i + 101}`,
isInstalled: i % 2 === 0,
chromeWebStoreUrl: `https://example.com/?chrome-store-${id}`,
googlePlayStoreUrl: `https://example.com/?google-play-${id}`,
iOSAppStoreUrl: `https://example.com/?ios-${id}`,
...(i % 2 === 0 && {
installPrompt: {
url: `https://example.com/?chrome-store-${id}`,
},
}),
};
}),
};
Expand Down
16 changes: 9 additions & 7 deletions src/lib/WalletProviderSelector/components/Divider.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
export function Divider() {
return (
<div
style={{
height: "100%",
width: "1px",
background: "#dcdcdc",
}}
/>
<div class="divider">
<div
style={{
height: "100%",
width: "1px",
background: "#dcdcdc",
}}
/>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ParentProps } from "solid-js";

export function RightPanelContainer(props: ParentProps) {
export function SidePanelContainer(props: ParentProps) {
return (
<div
style={{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ParentProps } from "solid-js";

export function RightPanelContentContainer(props: ParentProps) {
export function SidePanelContentContainer(props: ParentProps) {
return (
<div
style={{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { bodyTextStyles, titleTextStyles } from "../../styles";

export function RightPanelExplainer() {
export function SidePanelExplainer() {
return (
<div
style={{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
import { SupportedWallet } from "@sats-connect/core";

import {
bodyTextStyles,
buttonTextStyles,
titleTextStyles,
} from "../../styles";
import { TWalletProviderOption } from "../../utils";
import { openAppStore } from "../utils";

interface Props {
provider: SupportedWallet;
option: TWalletProviderOption;
}

export function RightPanelInstallWalletPrompt(props: Props) {
export function SidePanelInstallWalletPrompt(props: Props) {
function handleKeyDown(event: KeyboardEvent) {
if (event.key === "Enter" || event.key === " ") {
openAppStore(props.provider);
openAppStore(props.option);
}
}
function handleClick() {
openAppStore(props.provider);
openAppStore(props.option);
}
return (
<>
Expand All @@ -45,16 +44,16 @@ export function RightPanelInstallWalletPrompt(props: Props) {
width: "64px",
"object-fit": "cover",
}}
src={props.provider.icon}
alt={props.provider.name}
src={props.option.icon}
alt={props.option.name}
/>
<h1
style={{
...titleTextStyles,
"text-align": "center",
}}
>
Don't have {props.provider.name}?
Don't have {props.option.name}?
</h1>
<p style={bodyTextStyles}>Download it on the Chrome web store.</p>
<div
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { SupportedWallet } from "@sats-connect/core";

import { bodyTextStyles, titleTextStyles } from "../../styles";
import { TWalletProviderOption } from "../../utils";

import { Spinner } from "./Spinner";

interface Props {
provider: SupportedWallet;
option: TWalletProviderOption;
}

export function RightPanelOpeningWallet(props: Props) {
export function SidePanelOpeningWallet(props: Props) {
return (
<div
style={{
Expand All @@ -25,20 +24,18 @@ export function RightPanelOpeningWallet(props: Props) {
width: "64px",
"object-fit": "cover",
}}
src={props.provider.icon}
alt={props.provider.name}
src={props.option.icon}
alt={props.option.name}
/>
<h1
style={{
...titleTextStyles,
"text-align": "center",
}}
>
Opening {props.provider.name}...
Opening {props.option.name}...
</h1>
<p style={bodyTextStyles}>
Confirm the operation in {props.provider.name}
</p>
<p style={bodyTextStyles}>Confirm the operation in {props.option.name}</p>
<Spinner />
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { SupportedWallet } from "@sats-connect/core";
import { createMemo, createSignal } from "solid-js";

import { bodyTextStyles } from "../../styles";
import { TWalletProviderOption, hasInstallPrompt } from "../../utils";

interface Props extends SupportedWallet {
interface Props extends TWalletProviderOption {
onProviderSelected: (walletId: string) => void;
}

Expand All @@ -12,7 +12,7 @@ export function WalletProviderOption(props: Props) {
props.onProviderSelected(props.id);
}

const role = createMemo(() => (props.isInstalled ? "button" : "link"));
const role = createMemo(() => (hasInstallPrompt(props) ? "button" : "link"));

function handleKeyDown(e: KeyboardEvent) {
if (role() === "link") {
Expand Down
Loading