find taler action in clipboard and withdraw with mobile

This commit is contained in:
Sebastian 2022-09-09 12:22:26 -03:00
parent 9b2d6d766f
commit dda90b51f6
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
10 changed files with 127 additions and 43 deletions

View File

@ -25,9 +25,11 @@ import { platform } from "../platform/api.js";
interface Type {
findTalerUriInActiveTab: () => Promise<string | undefined>;
findTalerUriInClipboard: () => Promise<string | undefined>;
}
const Context = createContext<Type>({
findTalerUriInActiveTab: async () => undefined,
findTalerUriInClipboard: async () => undefined,
});
/**
@ -56,7 +58,10 @@ export const IoCProviderForRuntime = ({
children: any;
}): VNode => {
return h(Context.Provider, {
value: { findTalerUriInActiveTab: platform.findTalerUriInActiveTab },
value: {
findTalerUriInActiveTab: platform.findTalerUriInActiveTab,
findTalerUriInClipboard: platform.findTalerUriInClipboard,
},
children,
});
};

View File

@ -122,13 +122,13 @@ export function ReadyView({
}}
>
<i18n.Translate>Exchange</i18n.Translate>
<Link>
{/* <Link>
<SvgIcon
title="Edit"
dangerouslySetInnerHTML={{ __html: editIcon }}
color="black"
/>
</Link>
</Link> */}
</div>
}
text={<ExchangeDetails exchange={exchangeUrl} />}

View File

@ -85,6 +85,7 @@ export namespace State {
ageRestriction?: SelectFieldHandler;
talerWithdrawUri?: string;
cancel: () => Promise<void>;
};
}

View File

@ -410,6 +410,7 @@ export function useComponentStateFromURI(
toBeReceived,
withdrawalFee,
chosenAmount,
talerWithdrawUri,
ageRestriction,
doWithdrawal: {
onClick:

View File

@ -23,6 +23,7 @@ import { SelectList } from "../../components/SelectList.js";
import {
Input,
Link,
LinkSuccess,
SubTitle,
SuccessBox,
SvgIcon,
@ -35,6 +36,8 @@ import { TermsOfServiceSection } from "../TermsOfServiceSection.js";
import { State } from "./index.js";
import editIcon from "../../svg/edit_24px.svg";
import { Amount } from "../../components/Amount.js";
import { QR } from "../../components/QR.js";
import { useState } from "preact/hooks";
export function LoadingUriView({ error }: State.LoadingUriError): VNode {
const { i18n } = useTranslationContext();
@ -126,13 +129,13 @@ export function SuccessView(state: State.Success): VNode {
}}
>
<i18n.Translate>Exchange</i18n.Translate>
<Link>
{/* <Link>
<SvgIcon
title="Edit"
dangerouslySetInnerHTML={{ __html: editIcon }}
color="black"
/>
</Link>
</Link> */}
</div>
}
text={<ExchangeDetails exchange={state.exchangeUrl} />}
@ -164,31 +167,36 @@ export function SuccessView(state: State.Success): VNode {
</section>
{state.tosProps && <TermsOfServiceSection {...state.tosProps} />}
{state.tosProps ? (
<section>
{(state.tosProps.terms.status === "accepted" ||
(state.mustAcceptFirst && state.tosProps.reviewed)) && (
<Button
variant="contained"
color="success"
disabled={!state.doWithdrawal.onClick}
onClick={state.doWithdrawal.onClick}
>
<i18n.Translate>
Withdraw &nbsp; <Amount value={state.toBeReceived} />
</i18n.Translate>
</Button>
)}
{state.tosProps.terms.status === "notfound" && (
<Button
variant="contained"
color="warning"
disabled={!state.doWithdrawal.onClick}
onClick={state.doWithdrawal.onClick}
>
<i18n.Translate>Withdraw anyway</i18n.Translate>
</Button>
)}
</section>
<Fragment>
<section>
{(state.tosProps.terms.status === "accepted" ||
(state.mustAcceptFirst && state.tosProps.reviewed)) && (
<Button
variant="contained"
color="success"
disabled={!state.doWithdrawal.onClick}
onClick={state.doWithdrawal.onClick}
>
<i18n.Translate>
Withdraw &nbsp; <Amount value={state.toBeReceived} />
</i18n.Translate>
</Button>
)}
{state.tosProps.terms.status === "notfound" && (
<Button
variant="contained"
color="warning"
disabled={!state.doWithdrawal.onClick}
onClick={state.doWithdrawal.onClick}
>
<i18n.Translate>Withdraw anyway</i18n.Translate>
</Button>
)}
</section>
{state.talerWithdrawUri ? (
<WithdrawWithMobile talerWithdrawUri={state.talerWithdrawUri} />
) : undefined}
</Fragment>
) : (
<section>
<i18n.Translate>Loading terms of service...</i18n.Translate>
@ -202,3 +210,35 @@ export function SuccessView(state: State.Success): VNode {
</WalletAction>
);
}
function WithdrawWithMobile({
talerWithdrawUri,
}: {
talerWithdrawUri: string;
}): VNode {
const { i18n } = useTranslationContext();
const [showQR, setShowQR] = useState<boolean>(false);
return (
<section>
<LinkSuccess upperCased onClick={() => setShowQR((qr) => !qr)}>
{!showQR ? (
<i18n.Translate>Withdraw to a mobile phone</i18n.Translate>
) : (
<i18n.Translate>Hide QR</i18n.Translate>
)}
</LinkSuccess>
{showQR && (
<div>
<QR text={talerWithdrawUri} />
<i18n.Translate>
Scan the QR code or &nbsp;
<a href={talerWithdrawUri}>
<i18n.Translate>click here</i18n.Translate>
</a>
</i18n.Translate>
</div>
)}
</section>
);
}

View File

@ -17,20 +17,39 @@
import { useEffect, useState } from "preact/hooks";
import { useIocContext } from "../context/iocContext.js";
export interface UriLocation {
uri: string;
location: "clipboard" | "activeTab"
}
export function useTalerActionURL(): [
string | undefined,
UriLocation | undefined,
(s: boolean) => void,
] {
const [talerActionUrl, setTalerActionUrl] = useState<string | undefined>(
const [talerActionUrl, setTalerActionUrl] = useState<UriLocation | undefined>(
undefined,
);
const [dismissed, setDismissed] = useState(false);
const { findTalerUriInActiveTab } = useIocContext();
const { findTalerUriInActiveTab, findTalerUriInClipboard } = useIocContext();
useEffect(() => {
async function check(): Promise<void> {
const talerUri = await findTalerUriInActiveTab();
setTalerActionUrl(talerUri);
const clipUri = await findTalerUriInClipboard();
if (clipUri) {
setTalerActionUrl({
location: "clipboard",
uri: clipUri
});
return;
}
const tabUri = await findTalerUriInActiveTab();
if (tabUri) {
setTalerActionUrl({
location: "activeTab",
uri: tabUri
});
return;
}
}
check();
});

View File

@ -163,13 +163,22 @@ export interface PlatformAPI {
findTalerUriInActiveTab(): Promise<string | undefined>;
/**
* Used from the frontend to send commands to the wallet
* Popup API
*
* @param operation
* @param payload
* Read the current tab html and try to find any Taler URI or QR code present.
*
* @return response from the backend
* @return Taler URI if found
*/
findTalerUriInClipboard(): Promise<string | undefined>;
/**
* Used from the frontend to send commands to the wallet
*
* @param operation
* @param payload
*
* @return response from the backend
*/
sendMessageToWalletBackground(
operation: string,
payload: any,

View File

@ -30,6 +30,7 @@ import {
const api: PlatformAPI = {
isFirefox,
findTalerUriInActiveTab,
findTalerUriInClipboard,
getPermissionsApi,
getWalletWebExVersion,
listenToWalletBackground,
@ -689,6 +690,11 @@ async function findTalerUriInTab(tabId: number): Promise<string | undefined> {
}
}
async function findTalerUriInClipboard(): Promise<string | undefined> {
const textInClipboard = await window.navigator.clipboard.readText();
return textInClipboard.startsWith("taler://") || textInClipboard.startsWith("taler+http://") ? textInClipboard : undefined
}
async function findTalerUriInActiveTab(): Promise<string | undefined> {
const tab = await getCurrentTab();
if (!tab || tab.id === undefined) return;

View File

@ -23,6 +23,7 @@ const api: PlatformAPI = {
isFirefox: () => false,
keepAlive: (cb: VoidFunction) => cb(),
findTalerUriInActiveTab: async () => undefined,
findTalerUriInClipboard: async () => undefined,
containsTalerHeaderListener: () => {
return true;
},

View File

@ -42,13 +42,15 @@ import { BalancePage } from "./BalancePage.js";
import { TalerActionFound } from "./TalerActionFound.js";
function CheckTalerActionComponent(): VNode {
const [talerActionUrl] = useTalerActionURL();
const [action] = useTalerActionURL();
const actionUri = action?.uri;
useEffect(() => {
if (talerActionUrl) {
route(Pages.cta({ action: encodeURIComponent(talerActionUrl) }));
if (actionUri) {
route(Pages.cta({ action: encodeURIComponent(actionUri) }));
}
}, [talerActionUrl]);
}, [actionUri]);
return <Fragment />;
}