diff options
Diffstat (limited to 'packages')
10 files changed, 835 insertions, 124 deletions
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx index 69073f500..a3c46c78d 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx @@ -35,6 +35,405 @@ export default { }, }; +const termsHtml = `<html xmlns="http://www.w3.org/1999/xhtml" lang="en"> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <title>Terms Of Service — Taler Terms of Service</title> +</head><body> + <div> + Terms of service + </div> + <div> + A complete separated html with it's own design + </div> +</body> +</html> +` +const termsPlain = ` +Terms Of Service +**************** + +Last Updated: 12.4.2019 + +Welcome! Taler Systems SA (“we,” “our,” or “us”) provides a payment +service through our Internet presence (collectively the “Services”). +Before using our Services, please read the Terms of Service (the +“Terms” or the “Agreement”) carefully. + + +Overview +======== + +This section provides a brief summary of the highlights of this +Agreement. Please note that when you accept this Agreement, you are +accepting all of the terms and conditions and not just this section. +We and possibly other third parties provide Internet services which +interact with the Taler Wallet’s self-hosted personal payment +application. When using the Taler Wallet to interact with our +Services, you are agreeing to our Terms, so please read carefully. + + +Highlights: +----------- + + * You are responsible for keeping the data in your Taler Wallet at + all times under your control. Any losses arising from you not + being in control of your private information are your problem. + + * We will try to transfer funds we hold in escrow for our users to + any legal recipient to the best of our ability within the + limitations of the law and our implementation. However, the + Services offered today are highly experimental and the set of + recipients of funds is severely restricted. + + * For our Services, we may charge transaction fees. The specific + fee structure is provided based on the Taler protocol and should + be shown to you when you withdraw electronic coins using a Taler + Wallet. You agree and understand that the Taler protocol allows + for the fee structure to change. + + * You agree to not intentionally overwhelm our systems with + requests and follow responsible disclosure if you find security + issues in our services. + + * We cannot be held accountable for our Services not being + available due to circumstances beyond our control. If we modify + or terminate our services, we will try to give you the + opportunity to recover your funds. However, given the + experimental state of the Services today, this may not be + possible. You are strongly advised to limit your use of the + Service to small-scale experiments expecting total loss of all + funds. + +These terms outline approved uses of our Services. The Services and +these Terms are still at an experimental stage. If you have any +questions or comments related to this Agreement, please send us a +message to legal@taler-systems.com. If you do not agree to this +Agreement, you must not use our Services. + + +How you accept this policy +========================== + +By sending funds to us (to top-up your Taler Wallet), you acknowledge +that you have read, understood, and agreed to these Terms. We reserve +the right to change these Terms at any time. If you disagree with the +change, we may in the future offer you with an easy option to recover +your unspent funds. However, in the current experimental period you +acknowledge that this feature is not yet available, resulting in your +funds being lost unless you accept the new Terms. If you continue to +use our Services other than to recover your unspent funds, your +continued use of our Services following any such change will signify +your acceptance to be bound by the then current Terms. Please check +the effective date above to determine if there have been any changes +since you have last reviewed these Terms. + + +Services +======== + +We will try to transfer funds that we hold in escrow for our users to +any legal recipient to the best of our ability and within the +limitations of the law and our implementation. However, the Services +offered today are highly experimental and the set of recipients of +funds is severely restricted. The Taler Wallet can be loaded by +exchanging fiat currencies against electronic coins. We are providing +this exchange service. Once your Taler Wallet is loaded with +electronic coins they can be spent for purchases if the seller is +accepting Taler as a means of payment. We are not guaranteeing that +any seller is accepting Taler at all or a particular seller. The +seller or recipient of deposits of electronic coins must specify the +target account, as per the design of the Taler protocol. They are +responsible for following the protocol and specifying the correct bank +account, and are solely liable for any losses that may arise from +specifying the wrong account. We will allow the government to link +wire transfers to the underlying contract hash. It is the +responsibility of recipients to preserve the full contracts and to pay +whatever taxes and charges may be applicable. Technical issues may +lead to situations where we are unable to make transfers at all or +lead to incorrect transfers that cannot be reversed. We will only +refuse to execute transfers if the transfers are prohibited by a +competent legal authority and we are ordered to do so. + + +Fees +==== + +You agree to pay the fees for exchanges and withdrawals completed via +the Taler Wallet ("Fees") as defined by us, which we may change from +time to time. With the exception of wire transfer fees, Taler +transaction fees are set for any electronic coin at the time of +withdrawal and fixed throughout the validity period of the respective +electronic coin. Your wallet should obtain and display applicable fees +when withdrawing funds. Fees for coins obtained as change may differ +from the fees applicable to the original coin. Wire transfer fees that +are independent from electronic coins may change annually. You +authorize us to charge or deduct applicable fees owed in connection +with deposits, exchanges and withdrawals following the rules of the +Taler protocol. We reserve the right to provide different types of +rewards to users either in the form of discount for our Services or in +any other form at our discretion and without prior notice to you. + + +Eligibility +=========== + +To be eligible to use our Services, you must be able to form legally +binding contracts or have the permission of your legal guardian. By +using our Services, you represent and warrant that you meet all +eligibility requirements that we outline in these Terms. + + +Financial self-responsibility +============================= + +You will be responsible for maintaining the availability, integrity +and confidentiality of the data stored in your wallet. When you setup +a Taler Wallet, you are strongly advised to follow the precautionary +measures offered by the software to minimize the chances to losse +access to or control over your Wallet data. We will not be liable for +any loss or damage arising from your failure to comply with this +paragraph. + + +Copyrights and trademarks +========================= + +The Taler Wallet is released under the terms of the GNU General Public +License (GNU GPL). You have the right to access, use, and share the +Taler Wallet, in modified or unmodified form. However, the GPL is a +strong copyleft license, which means that any derivative works must be +distributed under the same license terms as the original software. If +you have any questions, you should review the GNU GPL’s full terms and +conditions at https://www.gnu.org/licenses/gpl-3.0.en.html. “Taler” +itself is a trademark of Taler Systems SA. You are welcome to use the +name in relation to processing payments using the Taler protocol, +assuming your use is compatible with an official release from the GNU +Project that is not older than two years. + + +Your use of our services +======================== + +When using our Services, you agree to not take any action that +intentionally imposes an unreasonable load on our infrastructure. If +you find security problems in our Services, you agree to first report +them to security@taler-systems.com and grant us the right to publish +your report. We warrant that we will ourselves publicly disclose any +issues reported within 3 months, and that we will not prosecute anyone +reporting security issues if they did not exploit the issue beyond a +proof-of-concept, and followed the above responsible disclosure +practice. + + +Limitation of liability & disclaimer of warranties +================================================== + +You understand and agree that we have no control over, and no duty to +take any action regarding: Failures, disruptions, errors, or delays in +processing that you may experience while using our Services; The risk +of failure of hardware, software, and Internet connections; The risk +of malicious software being introduced or found in the software +underlying the Taler Wallet; The risk that third parties may obtain +unauthorized access to information stored within your Taler Wallet, +including, but not limited to your Taler Wallet coins or backup +encryption keys. You release us from all liability related to any +losses, damages, or claims arising from: + +1. user error such as forgotten passwords, incorrectly constructed + transactions; + +2. server failure or data loss; + +3. unauthorized access to the Taler Wallet application; + +4. bugs or other errors in the Taler Wallet software; and + +5. any unauthorized third party activities, including, but not limited + to, the use of viruses, phishing, brute forcing, or other means of + attack against the Taler Wallet. We make no representations + concerning any Third Party Content contained in or accessed through + our Services. + +Any other terms, conditions, warranties, or representations associated +with such content, are solely between you and such organizations +and/or individuals. + + +Limitation of liability +======================= + +To the fullest extent permitted by applicable law, in no event will we +or any of our officers, directors, representatives, agents, servants, +counsel, employees, consultants, lawyers, and other personnel +authorized to act, acting, or purporting to act on our behalf +(collectively the “Taler Parties”) be liable to you under contract, +tort, strict liability, negligence, or any other legal or equitable +theory, for: + +1. any lost profits, data loss, cost of procurement of substitute + goods or services, or direct, indirect, incidental, special, + punitive, compensatory, or consequential damages of any kind + whatsoever resulting from: + + 1. your use of, or conduct in connection with, our services; + + 2. any unauthorized use of your wallet and/or private key due to + your failure to maintain the confidentiality of your wallet; + + 3. any interruption or cessation of transmission to or from the + services; or + + 4. any bugs, viruses, trojan horses, or the like that are found in + the Taler Wallet software or that may be transmitted to or + through our services by any third party (regardless of the + source of origination), or + +2. any direct damages. + +These limitations apply regardless of legal theory, whether based on +tort, strict liability, breach of contract, breach of warranty, or any +other legal theory, and whether or not we were advised of the +possibility of such damages. Some jurisdictions do not allow the +exclusion or limitation of liability for consequential or incidental +damages, so the above limitation may not apply to you. + + +Warranty disclaimer +=================== + +Our services are provided "as is" and without warranty of any kind. To +the maximum extent permitted by law, we disclaim all representations +and warranties, express or implied, relating to the services and +underlying software or any content on the services, whether provided +or owned by us or by any third party, including without limitation, +warranties of merchantability, fitness for a particular purpose, +title, non-infringement, freedom from computer virus, and any implied +warranties arising from course of dealing, course of performance, or +usage in trade, all of which are expressly disclaimed. In addition, we +do not represent or warrant that the content accessible via the +services is accurate, complete, available, current, free of viruses or +other harmful components, or that the results of using the services +will meet your requirements. Some states do not allow the disclaimer +of implied warranties, so the foregoing disclaimers may not apply to +you. This paragraph gives you specific legal rights and you may also +have other legal rights that vary from state to state. + + +Indemnity +========= + +To the extent permitted by applicable law, you agree to defend, +indemnify, and hold harmless the Taler Parties from and against any +and all claims, damages, obligations, losses, liabilities, costs or +debt, and expenses (including, but not limited to, attorney’s fees) +arising from: (a) your use of and access to the Services; (b) any +feedback or submissions you provide to us concerning the Taler Wallet; +(c) your violation of any term of this Agreement; or (d) your +violation of any law, rule, or regulation, or the rights of any third +party. + + +Time limitation on claims +========================= + +You agree that any claim you may have arising out of or related to +your relationship with us must be filed within one year after such +claim arises, otherwise, your claim in permanently barred. + + +Governing law +============= + +No matter where you’re located, the laws of Switzerland will govern +these Terms. If any provisions of these Terms are inconsistent with +any applicable law, those provisions will be superseded or modified +only to the extent such provisions are inconsistent. The parties agree +to submit to the ordinary courts in Zurich, Switzerland for exclusive +jurisdiction of any dispute arising out of or related to your use of +the Services or your breach of these Terms. + + +Termination +=========== + +In the event of termination concerning your use of our Services, your +obligations under this Agreement will still continue. + + +Discontinuance of services +========================== + +We may, in our sole discretion and without cost to you, with or +without prior notice, and at any time, modify or discontinue, +temporarily or permanently, any portion of our Services. We will use +the Taler protocol’s provisions to notify Wallets if our Services are +to be discontinued. It is your responsibility to ensure that the Taler +Wallet is online at least once every three months to observe these +notifications. We shall not be held responsible or liable for any loss +of funds in the event that we discontinue or depreciate the Services +and your Taler Wallet fails to transfer out the coins within a three +months notification period. + + +No waiver +========= + +Our failure to exercise or delay in exercising any right, power, or +privilege under this Agreement shall not operate as a waiver; nor +shall any single or partial exercise of any right, power, or privilege +preclude any other or further exercise thereof. + + +Severability +============ + +If it turns out that any part of this Agreement is invalid, void, or +for any reason unenforceable, that term will be deemed severable and +limited or eliminated to the minimum extent necessary. + + +Force majeure +============= + +We shall not be held liable for any delays, failure in performance, or +interruptions of service which result directly or indirectly from any +cause or condition beyond our reasonable control, including but not +limited to: any delay or failure due to any act of God, act of civil +or military authorities, act of terrorism, civil disturbance, war, +strike or other labor dispute, fire, interruption in +telecommunications or Internet services or network provider services, +failure of equipment and/or software, other catastrophe, or any other +occurrence which is beyond our reasonable control and shall not affect +the validity and enforceability of any remaining provisions. + + +Assignment +========== + +You agree that we may assign any of our rights and/or transfer, sub- +contract, or delegate any of our obligations under these Terms. + + +Entire agreement +================ + +This Agreement sets forth the entire understanding and agreement as to +the subject matter hereof and supersedes any and all prior +discussions, agreements, and understandings of any kind (including, +without limitation, any prior versions of this Agreement) and every +nature between us. Except as provided for above, any modification to +this Agreement must be in writing and must be signed by both parties. + + +Questions or comments +===================== + +We welcome comments, questions, concerns, or suggestions. Please send +us a message on our contact page at legal@taler-systems.com. + +` + const termsXml = `<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE document PUBLIC "+//IDN docutils.sourceforge.net//DTD Docutils Generic//EN//XML" "http://docutils.sourceforge.net/docs/ref/docutils.dtd"> <!-- Generated by Docutils 0.14 --> @@ -381,7 +780,7 @@ const termsXml = `<?xml version="1.0" encoding="utf-8"?> </document> `; -export const WithdrawNewTermsXML = createExample(TestedComponent, { +export const NewTerms = createExample(TestedComponent, { knownExchanges: [{ currency: 'USD', exchangeBaseUrl: 'exchange.demo.taler.net', @@ -417,7 +816,134 @@ export const WithdrawNewTermsXML = createExample(TestedComponent, { }, }) -export const WithdrawNewTermsReviewingXML = createExample(TestedComponent, { +export const TermsReviewingPLAIN = createExample(TestedComponent, { + knownExchanges: [{ + currency: 'USD', + exchangeBaseUrl: 'exchange.demo.taler.net', + paytoUris: ['asd'], + },{ + currency: 'USD', + exchangeBaseUrl: 'exchange.test.taler.net', + paytoUris: ['asd'], + }], + details: { + exchangeInfo: { + baseUrl: 'exchange.demo.taler.net' + } as ExchangeRecord, + withdrawFee: { + currency: 'USD', + fraction: 0, + value: 0 + }, + } as ExchangeWithdrawDetails, + amount: { + currency: 'USD', + value: 2, + fraction: 10000000 + }, + + onSwitchExchange: async () => { }, + terms: { + value: { + type: 'plain', + content: termsPlain + }, + status: 'new' + }, + reviewing: true +}) + +export const TermsReviewingHTML = createExample(TestedComponent, { + knownExchanges: [{ + currency: 'USD', + exchangeBaseUrl: 'exchange.demo.taler.net', + paytoUris: ['asd'], + },{ + currency: 'USD', + exchangeBaseUrl: 'exchange.test.taler.net', + paytoUris: ['asd'], + }], + details: { + exchangeInfo: { + baseUrl: 'exchange.demo.taler.net' + } as ExchangeRecord, + withdrawFee: { + currency: 'USD', + fraction: 0, + value: 0 + }, + } as ExchangeWithdrawDetails, + amount: { + currency: 'USD', + value: 2, + fraction: 10000000 + }, + + onSwitchExchange: async () => { }, + terms: { + value: { + type: 'html', + href: new URL(`data:text/html;base64,${Buffer.from(termsHtml).toString('base64')}`), + }, + status: 'new' + }, + reviewing: true +}) + +const termsPdf = ` +%PDF-1.2 +9 0 obj << >> +stream +BT/ 9 Tf(This is the Exchange TERMS OF SERVICE)' ET +endstream +endobj +4 0 obj << /Type /Page /Parent 5 0 R /Contents 9 0 R >> endobj +5 0 obj << /Kids [4 0 R ] /Count 1 /Type /Pages /MediaBox [ 0 0 180 20 ] >> endobj +3 0 obj << /Pages 5 0 R /Type /Catalog >> endobj +trailer +<< /Root 3 0 R >> +%%EOF +` + +export const TermsReviewingPDF = createExample(TestedComponent, { + knownExchanges: [{ + currency: 'USD', + exchangeBaseUrl: 'exchange.demo.taler.net', + paytoUris: ['asd'], + },{ + currency: 'USD', + exchangeBaseUrl: 'exchange.test.taler.net', + paytoUris: ['asd'], + }], + details: { + exchangeInfo: { + baseUrl: 'exchange.demo.taler.net' + } as ExchangeRecord, + withdrawFee: { + currency: 'USD', + fraction: 0, + value: 0 + }, + } as ExchangeWithdrawDetails, + amount: { + currency: 'USD', + value: 2, + fraction: 10000000 + }, + + onSwitchExchange: async () => { }, + terms: { + value: { + type: 'pdf', + location: new URL(`data:text/html;base64,${Buffer.from(termsPdf).toString('base64')}`), + }, + status: 'new' + }, + reviewing: true +}) + + +export const TermsReviewingXML = createExample(TestedComponent, { knownExchanges: [{ currency: 'USD', exchangeBaseUrl: 'exchange.demo.taler.net', @@ -454,7 +980,7 @@ export const WithdrawNewTermsReviewingXML = createExample(TestedComponent, { reviewing: true }) -export const WithdrawNewTermsAcceptedXML = createExample(TestedComponent, { +export const NewTermsAccepted = createExample(TestedComponent, { knownExchanges: [{ currency: 'USD', exchangeBaseUrl: 'exchange.demo.taler.net', @@ -487,10 +1013,10 @@ export const WithdrawNewTermsAcceptedXML = createExample(TestedComponent, { }, status: 'new' }, - accepted: true + reviewed: true }) -export const WithdrawNewTermsShowAfterAcceptedXML = createExample(TestedComponent, { +export const TermsShowAgainXML = createExample(TestedComponent, { knownExchanges: [{ currency: 'USD', exchangeBaseUrl: 'exchange.demo.taler.net', @@ -528,7 +1054,7 @@ export const WithdrawNewTermsShowAfterAcceptedXML = createExample(TestedComponen reviewing: true, }) -export const WithdrawChangedTermsXML = createExample(TestedComponent, { +export const TermsChanged = createExample(TestedComponent, { knownExchanges: [{ currency: 'USD', exchangeBaseUrl: 'exchange.demo.taler.net', @@ -564,7 +1090,7 @@ export const WithdrawChangedTermsXML = createExample(TestedComponent, { }, }) -export const WithdrawNotFoundTermsXML = createExample(TestedComponent, { +export const TermsNotFound = createExample(TestedComponent, { knownExchanges: [{ currency: 'USD', exchangeBaseUrl: 'exchange.demo.taler.net', @@ -596,7 +1122,7 @@ export const WithdrawNotFoundTermsXML = createExample(TestedComponent, { }, }) -export const WithdrawAcceptedTermsXML = createExample(TestedComponent, { +export const TermsAlreadyAccepted = createExample(TestedComponent, { knownExchanges: [{ currency: 'USD', exchangeBaseUrl: 'exchange.demo.taler.net', @@ -629,7 +1155,7 @@ export const WithdrawAcceptedTermsXML = createExample(TestedComponent, { }) -export const WithdrawAcceptedTermsWithoutFee = createExample(TestedComponent, { +export const WithoutFee = createExample(TestedComponent, { knownExchanges: [{ currency: 'USD', exchangeBaseUrl: 'exchange.demo.taler.net', diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx index 52295f1af..94f217fc7 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx +++ b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx @@ -49,7 +49,7 @@ export interface ViewProps { onReview: (b: boolean) => void; onAccept: (b: boolean) => void; reviewing: boolean; - accepted: boolean; + reviewed: boolean; confirmed: boolean; terms: { value?: TermsDocument; @@ -61,7 +61,7 @@ export interface ViewProps { type TermsStatus = 'new' | 'accepted' | 'changed' | 'notfound'; -type TermsDocument = TermsDocumentXml | TermsDocumentHtml; +type TermsDocument = TermsDocumentXml | TermsDocumentHtml | TermsDocumentPlain | TermsDocumentJson | TermsDocumentPdf; interface TermsDocumentXml { type: 'xml', @@ -70,7 +70,22 @@ interface TermsDocumentXml { interface TermsDocumentHtml { type: 'html', - href: string, + href: URL, +} + +interface TermsDocumentPlain { + type: 'plain', + content: string, +} + +interface TermsDocumentJson { + type: 'json', + data: any, +} + +interface TermsDocumentPdf { + type: 'pdf', + location: URL, } function amountToString(text: AmountJson) { @@ -79,7 +94,7 @@ function amountToString(text: AmountJson) { return `${amount} ${aj.currency}` } -export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExchange, terms, reviewing, onReview, onAccept, accepted, confirmed }: ViewProps) { +export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExchange, terms, reviewing, onReview, onAccept, reviewed, confirmed }: ViewProps) { const needsReview = terms.status === 'changed' || terms.status === 'new' const [switchingExchange, setSwitchingExchange] = useState<string | undefined>(undefined) @@ -105,9 +120,6 @@ export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExch <div> <SelectList label="Known exchanges" list={exchanges} name="" onChange={onSwitchExchange} /> </div> - <p> - This is the list of known exchanges - </p> <LinkSuccess upperCased onClick={() => onSwitchExchange(switchingExchange)}> {i18n.str`Confirm exchange selection`} </LinkSuccess> @@ -118,7 +130,7 @@ export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExch </section> } - {!reviewing && accepted && + {!reviewing && reviewed && <section> <LinkSuccess upperCased @@ -130,11 +142,24 @@ export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExch } {reviewing && <section> - <TermsOfService> - {terms.status !== 'accepted' && terms.value && terms.value.type === 'xml' && <ExchangeXmlTos doc={terms.value.document} />} - </TermsOfService> + {terms.status !== 'accepted' && terms.value && terms.value.type === 'xml' && + <TermsOfService> + <ExchangeXmlTos doc={terms.value.document} /> + </TermsOfService> + } + {terms.status !== 'accepted' && terms.value && terms.value.type === 'plain' && + <div style={{ textAlign: 'left' }}> + <pre>{terms.value.content}</pre> + </div> + } + {terms.status !== 'accepted' && terms.value && terms.value.type === 'html' && + <iframe src={terms.value.href.toString()} /> + } + {terms.status !== 'accepted' && terms.value && terms.value.type === 'pdf' && + <a href={terms.value.location.toString()} download="tos.pdf" >Download Terms of Service</a> + } </section>} - {reviewing && accepted && + {reviewing && reviewed && <section> <LinkSuccess upperCased @@ -144,14 +169,14 @@ export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExch </LinkSuccess> </section> } - {(reviewing || accepted) && + {(reviewing || reviewed) && <section> <CheckboxOutlined name="terms" - enabled={accepted} + enabled={reviewed} label={i18n.str`I accept the exchange terms of service`} onToggle={() => { - onAccept(!accepted) + onAccept(!reviewed) onReview(false) }} /> @@ -162,7 +187,7 @@ export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExch * Main action section */} <section> - {terms.status === 'new' && !accepted && !reviewing && + {terms.status === 'new' && !reviewed && !reviewing && <ButtonSuccess upperCased disabled={!details.exchangeInfo.baseUrl} @@ -171,7 +196,7 @@ export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExch {i18n.str`Review exchange terms of service`} </ButtonSuccess> } - {terms.status === 'changed' && !accepted && + {terms.status === 'changed' && !reviewed && !reviewing && <ButtonWarning upperCased disabled={!details.exchangeInfo.baseUrl} @@ -180,7 +205,7 @@ export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExch {i18n.str`Review new version of terms of service`} </ButtonWarning> } - {(terms.status === 'accepted' || (needsReview && accepted)) && + {(terms.status === 'accepted' || (needsReview && reviewed)) && <ButtonSuccess upperCased disabled={!details.exchangeInfo.baseUrl || confirmed} @@ -204,7 +229,7 @@ export function WithdrawPageWithParsedURI({ uri, uriInfo }: { uri: string, uriIn const [errorAccepting, setErrorAccepting] = useState<string | undefined>(undefined) const [reviewing, setReviewing] = useState<boolean>(false) - const [accepted, setAccepted] = useState<boolean>(false) + const [reviewed, setReviewed] = useState<boolean>(false) const [confirmed, setConfirmed] = useState<boolean>(false) const knownExchangesHook = useAsyncAsHook(() => listExchanges()) @@ -219,7 +244,7 @@ export function WithdrawPageWithParsedURI({ uri, uriInfo }: { uri: string, uriIn return getExchangeWithdrawalInfo({ exchangeBaseUrl: exchange, amount: withdrawAmount, - tosAcceptedFormat: ['text/json', 'text/xml', 'text/pdf'] + tosAcceptedFormat: ['text/xml'] }) }) @@ -235,7 +260,7 @@ export function WithdrawPageWithParsedURI({ uri, uriInfo }: { uri: string, uriIn const onAccept = async (): Promise<void> => { try { await setExchangeTosAccepted(details.exchangeInfo.baseUrl, details.tosRequested?.tosEtag) - setAccepted(true) + setReviewed(true) } catch (e) { if (e instanceof Error) { setErrorAccepting(e.message) @@ -257,18 +282,7 @@ export function WithdrawPageWithParsedURI({ uri, uriInfo }: { uri: string, uriIn } }; - let termsContent: TermsDocument | undefined = undefined; - if (details.tosRequested) { - if (details.tosRequested.tosContentType === 'text/xml') { - try { - const document = new DOMParser().parseFromString(details.tosRequested.tosText, "text/xml") - termsContent = { type: 'xml', document } - } catch (e) { - console.log(e) - debugger; - } - } - } + const termsContent: TermsDocument | undefined = !details.tosRequested ? undefined : parseTermsOfServiceContent(details.tosRequested.tosContentType, details.tosRequested.tosText); const status: TermsStatus = !termsContent ? 'notfound' : ( !details.exchangeDetails.termsOfServiceAcceptedEtag ? 'new' : ( @@ -285,7 +299,7 @@ export function WithdrawPageWithParsedURI({ uri, uriInfo }: { uri: string, uriIn onSwitchExchange={setCustomExchange} knownExchanges={knownExchanges} confirmed={confirmed} - accepted={accepted} onAccept={onAccept} + reviewed={reviewed} onAccept={onAccept} reviewing={reviewing} onReview={setReviewing} // terms={[]} /> @@ -307,3 +321,48 @@ export function WithdrawPage({ talerWithdrawUri }: Props): JSX.Element { return <WithdrawPageWithParsedURI uri={talerWithdrawUri} uriInfo={uriInfoHook.response} /> } +function parseTermsOfServiceContent(type: string, text: string): TermsDocument | undefined { + if (type === 'text/xml') { + try { + const document = new DOMParser().parseFromString(text, "text/xml") + return { type: 'xml', document } + } catch (e) { + console.log(e) + debugger; + } + } else if (type === 'text/html') { + try { + const href = new URL(text) + return { type: 'html', href } + } catch (e) { + console.log(e) + debugger; + } + } else if (type === 'text/json') { + try { + const data = JSON.parse(text) + return { type: 'json', data } + } catch (e) { + console.log(e) + debugger; + } + } else if (type === 'text/pdf') { + try { + const location = new URL(text) + return { type: 'pdf', location } + } catch (e) { + console.log(e) + debugger; + } + } else if (type === 'text/plain') { + try { + const content = text + return { type: 'plain', content } + } catch (e) { + console.log(e) + debugger; + } + } + return undefined +} + diff --git a/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts b/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts index 3178374f8..ff9cc029a 100644 --- a/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts +++ b/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts @@ -25,10 +25,7 @@ export function useTalerActionURL(): [string | undefined, (s: boolean) => void] useEffect(() => { async function check(): Promise<void> { const talerUri = await findTalerUriInActiveTab(); - if (talerUri) { - const actionUrl = actionForTalerUri(talerUri); - setTalerActionUrl(actionUrl); - } + setTalerActionUrl(talerUri) } check(); }, []); @@ -36,49 +33,6 @@ export function useTalerActionURL(): [string | undefined, (s: boolean) => void] return [url, setDismissed]; } -function actionForTalerUri(talerUri: string): string | undefined { - const uriType = classifyTalerUri(talerUri); - switch (uriType) { - case TalerUriType.TalerWithdraw: - return makeExtensionUrlWithParams("static/wallet.html#/withdraw", { - talerWithdrawUri: talerUri, - }); - case TalerUriType.TalerPay: - return makeExtensionUrlWithParams("static/wallet.html#/pay", { - talerPayUri: talerUri, - }); - case TalerUriType.TalerTip: - return makeExtensionUrlWithParams("static/wallet.html#/tip", { - talerTipUri: talerUri, - }); - case TalerUriType.TalerRefund: - return makeExtensionUrlWithParams("static/wallet.html#/refund", { - talerRefundUri: talerUri, - }); - case TalerUriType.TalerNotifyReserve: - // FIXME: implement - break; - default: - console.warn( - "Response with HTTP 402 has Taler header, but header value is not a taler:// URI.", - ); - break; - } - return undefined; -} - -function makeExtensionUrlWithParams( - url: string, - params?: { [name: string]: string | undefined }, -): string { - const innerUrl = new URL(chrome.extension.getURL("/" + url)); - if (params) { - const hParams = Object.keys(params).map(k => `${k}=${params[k]}`).join('&') - innerUrl.hash = innerUrl.hash + '?' + hParams - } - return innerUrl.href; -} - async function findTalerUriInActiveTab(): Promise<string | undefined> { return new Promise((resolve, reject) => { chrome.tabs.executeScript( diff --git a/packages/taler-wallet-webextension/src/popup/TalerActionFound.stories.tsx b/packages/taler-wallet-webextension/src/popup/TalerActionFound.stories.tsx new file mode 100644 index 000000000..88c7c725e --- /dev/null +++ b/packages/taler-wallet-webextension/src/popup/TalerActionFound.stories.tsx @@ -0,0 +1,52 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { createExample } from '../test-utils'; +import { TalerActionFound as TestedComponent } from './TalerActionFound'; + +export default { + title: 'popup/TalerActionFound', + component: TestedComponent, +}; + +export const PayAction = createExample(TestedComponent, { + url: 'taler://pay/something' +}); + +export const WithdrawalAction = createExample(TestedComponent, { + url: 'taler://withdraw/something' +}); + +export const TipAction = createExample(TestedComponent, { + url: 'taler://tip/something' +}); + +export const NotifyAction = createExample(TestedComponent, { + url: 'taler://notify-reserve/something' +}); + +export const RefundAction = createExample(TestedComponent, { + url: 'taler://refund/something' +}); + +export const InvalidAction = createExample(TestedComponent, { + url: 'taler://something/asd' +}); diff --git a/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx b/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx new file mode 100644 index 000000000..4e3c71682 --- /dev/null +++ b/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx @@ -0,0 +1,98 @@ +import { classifyTalerUri, TalerUriType } from "@gnu-taler/taler-util"; +import { ButtonPrimary, ButtonSuccess, PopupBox } from "../components/styled/index"; + +export interface Props { + url: string; + onDismiss: (s: boolean) => void; +} + +export function TalerActionFound({ url, onDismiss }: Props) { + const uriType = classifyTalerUri(url); + return <PopupBox> + <section> + <h1>Taler Action </h1> + {uriType === TalerUriType.TalerPay && <div> + <p>This page has pay action.</p> + <ButtonSuccess onClick={() => { chrome.tabs.create({ "url": actionForTalerUri(uriType, url) }); }}> + Open pay page + </ButtonSuccess> + </div>} + {uriType === TalerUriType.TalerWithdraw && <div> + <p>This page has a withdrawal action.</p> + <ButtonSuccess onClick={() => { chrome.tabs.create({ "url": actionForTalerUri(uriType, url) }); }}> + Open withdraw page + </ButtonSuccess> + </div>} + {uriType === TalerUriType.TalerTip && <div> + <p>This page has a tip action.</p> + <ButtonSuccess onClick={() => { chrome.tabs.create({ "url": actionForTalerUri(uriType, url) }); }}> + Open tip page + </ButtonSuccess> + </div>} + {uriType === TalerUriType.TalerNotifyReserve && <div> + <p>This page has a notify reserve action.</p> + <ButtonSuccess onClick={() => { chrome.tabs.create({ "url": actionForTalerUri(uriType, url) }); }}> + Notify + </ButtonSuccess> + </div>} + {uriType === TalerUriType.TalerRefund && <div> + <p>This page has a refund action.</p> + <ButtonSuccess onClick={() => { chrome.tabs.create({ "url": actionForTalerUri(uriType, url) }); }}> + Open refund page + </ButtonSuccess> + </div>} + {uriType === TalerUriType.Unknown && <div> + <p>This page has a malformed taler uri.</p> + <p>{url}</p> + </div>} + + </section> + <footer> + <div /> + <ButtonPrimary onClick={() => onDismiss(true)}> Dismiss </ButtonPrimary> + </footer> + </PopupBox>; + +} + +function actionForTalerUri(uriType: TalerUriType, talerUri: string): string | undefined { + switch (uriType) { + case TalerUriType.TalerWithdraw: + return makeExtensionUrlWithParams("static/wallet.html#/withdraw", { + talerWithdrawUri: talerUri, + }); + case TalerUriType.TalerPay: + return makeExtensionUrlWithParams("static/wallet.html#/pay", { + talerPayUri: talerUri, + }); + case TalerUriType.TalerTip: + return makeExtensionUrlWithParams("static/wallet.html#/tip", { + talerTipUri: talerUri, + }); + case TalerUriType.TalerRefund: + return makeExtensionUrlWithParams("static/wallet.html#/refund", { + talerRefundUri: talerUri, + }); + case TalerUriType.TalerNotifyReserve: + // FIXME: implement + break; + default: + console.warn( + "Response with HTTP 402 has Taler header, but header value is not a taler:// URI.", + ); + break; + } + return undefined; +} + +function makeExtensionUrlWithParams( + url: string, + params?: { [name: string]: string | undefined }, +): string { + const innerUrl = new URL(chrome.extension.getURL("/" + url)); + if (params) { + const hParams = Object.keys(params).map(k => `${k}=${params[k]}`).join('&') + innerUrl.hash = innerUrl.hash + '?' + hParams + } + return innerUrl.href; +} diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx index 4bdc2d888..33a462142 100644 --- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx @@ -38,6 +38,7 @@ import { import { ProviderAddPage } from "./popup/ProviderAddPage"; import { ProviderDetailPage } from "./popup/ProviderDetailPage"; import { SettingsPage } from "./popup/Settings"; +import { TalerActionFound } from "./popup/TalerActionFound"; function main(): void { try { @@ -62,32 +63,16 @@ if (document.readyState === "loading") { main(); } -interface Props { - url: string; - onDismiss: (s: boolean) => void; -} - -function TalerActionFound({ url, onDismiss }: Props) { - return <div style={{ padding: "1em", width: 400 }}> - <h1>Taler Action </h1> - <p>This page has a Taler action.</p> - <p> - <button onClick={() => { chrome.tabs.create({ "url": url }); }}> - Open - </button> - </p> - <p> - <button onClick={() => onDismiss(true)}> Dismiss </button> - </p> - </div> - -} - function Application() { const [talerActionUrl, setDismissed] = useTalerActionURL() if (talerActionUrl) { - return <TalerActionFound url={talerActionUrl} onDismiss={setDismissed} /> + return <div> + <WalletNavBar /> + <div style={{ width: 400, height: 290 }}> + <TalerActionFound url={talerActionUrl} onDismiss={setDismissed} /> + </div> + </div> } return ( diff --git a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx index d4daefc2e..35cea2b9f 100644 --- a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx @@ -21,6 +21,8 @@ import { CreateManualWithdraw } from "./CreateManualWithdraw"; import * as wxApi from '../wxApi' import { AcceptManualWithdrawalResult, AmountJson, Amounts } from "@gnu-taler/taler-util"; import { ReserveCreated } from "./ReserveCreated.js"; +import { route } from 'preact-router'; +import { Pages } from "../NavigationBar.js"; interface Props { @@ -58,7 +60,9 @@ export function ManualWithdrawPage({ }: Props): VNode { } if (success) { - return <ReserveCreated reservePub={success.reservePub} paytos={success.exchangePaytoUris} onBack={() => {}}/> + return <ReserveCreated reservePub={success.reservePub} paytos={success.exchangePaytoUris} onBack={() => { + route(Pages.balance) + }}/> } return <CreateManualWithdraw diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx index deb30e55f..a04a0b4fd 100644 --- a/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx @@ -41,3 +41,13 @@ export const OneChecked = createExample(TestedComponent, { setDeviceName: () => Promise.resolve(), }); +export const WithOneExchange = createExample(TestedComponent, { + deviceName: 'this-is-the-device-name', + permissionsEnabled: true, + setDeviceName: () => Promise.resolve(), + knownExchanges: [{ + currency: 'USD', + exchangeBaseUrl: 'http://exchange.taler', + paytoUris: ['payto://x-taler-bank/bank.rpi.sebasjm.com/exchangeminator'] + }] +}); diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.tsx b/packages/taler-wallet-webextension/src/wallet/Settings.tsx index d1eb012fc..8d18586b1 100644 --- a/packages/taler-wallet-webextension/src/wallet/Settings.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Settings.tsx @@ -81,15 +81,14 @@ export function SettingsView({ knownExchanges, lang, changeLang, deviceName, set {!knownExchanges || !knownExchanges.length ? <div> No exchange yet! </div> : - <dl> - {knownExchanges.map(e => <Fragment> - <dt>{e.currency}</dt> - <dd>{e.exchangeBaseUrl}</dd> - <dd>{e.paytoUris}</dd> - </Fragment>)} - </dl> + <table> + {knownExchanges.map(e => <tr> + <td>{e.currency}</td> + <td><a href={e.exchangeBaseUrl}>{e.exchangeBaseUrl}</a></td> + </tr>)} + </table> } - <ButtonPrimary>add exchange</ButtonPrimary> + <h2><i18n.Translate>Permissions</i18n.Translate></h2> <Checkbox label="Automatically open wallet based on page content" name="perm" @@ -105,4 +104,4 @@ export function SettingsView({ knownExchanges, lang, changeLang, deviceName, set </section> </WalletBox> ) -}
\ No newline at end of file +} diff --git a/packages/taler-wallet-webextension/static/popup.html b/packages/taler-wallet-webextension/static/popup.html index 9c1246456..e3c0b1589 100644 --- a/packages/taler-wallet-webextension/static/popup.html +++ b/packages/taler-wallet-webextension/static/popup.html @@ -2,8 +2,32 @@ <html> <head> <meta charset="utf-8" /> - <link rel="stylesheet" type="text/css" href="/static/style/pure.css" /> - <link rel="stylesheet" type="text/css" href="/static/style/popup.css" /> + <style> + html { + font-family: sans-serif; /* 1 */ + } + body { + margin: 0; + } + </style> + <style> + html { + } + h1 { + font-size: 2em; + } + input { + font: inherit; + } + body { + margin: 0; + font-size: 100%; + padding: 0; + overflow: hidden; + background-color: #f8faf7; + font-family: Arial, Helvetica, sans-serif; + } + </style> <link rel="stylesheet" type="text/css" href="/dist/popupEntryPoint.css" /> <link rel="icon" href="/static/img/icon.png" /> <script src="/dist/popupEntryPoint.js"></script> |