gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated (77c71a4c2 -> a1af7945d)


From: gnunet
Subject: [taler-wallet-core] branch master updated (77c71a4c2 -> a1af7945d)
Date: Mon, 13 Mar 2023 04:02:06 +0100

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a change to branch master
in repository wallet-core.

    from 77c71a4c2 check if threshold is number or amount and default to 0
     new ae1aee135 kyc 1min
     new b874f9a0c print and setup totp
     new a1af7945d fix broken build

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 packages/demobank-ui/src/pages/AdminPage.tsx       | 11 +++-
 .../src/components/form/InputCurrency.tsx          |  2 +-
 .../src/components/form/InputSelector.tsx          |  7 ++-
 .../merchant-backoffice-ui/src/declaration.d.ts    |  9 +++
 .../merchant-backoffice-ui/src/hooks/instance.ts   |  4 +-
 .../merchant-backoffice-ui/src/hooks/templates.ts  |  6 +-
 .../paths/instance/templates/create/CreatePage.tsx | 43 +++++++++++--
 .../src/paths/instance/templates/qr/QrPage.tsx     | 65 ++++++++++++++++++--
 .../src/paths/instance/templates/qr/index.tsx      |  2 -
 .../paths/instance/templates/update/UpdatePage.tsx | 42 +++++++++++--
 .../src/paths/instance/templates/use/UsePage.tsx   | 19 +++++-
 .../src/paths/instance/templates/use/index.tsx     |  1 +
 .../src/{scss/_form.scss => utils/crypto.ts}       | 70 ++++++++--------------
 13 files changed, 212 insertions(+), 69 deletions(-)
 copy packages/merchant-backoffice-ui/src/{scss/_form.scss => utils/crypto.ts} 
(50%)

diff --git a/packages/demobank-ui/src/pages/AdminPage.tsx 
b/packages/demobank-ui/src/pages/AdminPage.tsx
index 3dd34d251..f565455bb 100644
--- a/packages/demobank-ui/src/pages/AdminPage.tsx
+++ b/packages/demobank-ui/src/pages/AdminPage.tsx
@@ -224,8 +224,13 @@ export function AdminPage({ onLoadNotOk }: Props): VNode {
       return onLoadNotOk(result);
     }
     const { data } = result;
-    const balance = Amounts.parse(data.balance.amount);
-    const balanceIsDebit = data.balance.credit_debit_indicator == "debit";
+    const balance = Amounts.parseOrThrow(data.balance.amount);
+    const debitThreshold = Amounts.parseOrThrow(result.data.debitThreshold);
+    const balanceIsDebit =
+      result.data.balance.credit_debit_indicator == "debit";
+    const limit = balanceIsDebit
+      ? Amounts.sub(debitThreshold, balance).amount
+      : Amounts.add(balance, debitThreshold).amount;
     if (!balance) return <Fragment />;
     return (
       <Fragment>
@@ -250,7 +255,7 @@ export function AdminPage({ onLoadNotOk }: Props): VNode {
         </section>
         <PaytoWireTransferForm
           focus
-          currency={balance.currency}
+          limit={limit}
           onSuccess={() => {
             pageStateSetter((prevState: PageStateType) => ({
               ...prevState,
diff --git 
a/packages/merchant-backoffice-ui/src/components/form/InputCurrency.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputCurrency.tsx
index 3b84855da..b02354d7c 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputCurrency.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputCurrency.tsx
@@ -58,7 +58,7 @@ export function InputCurrency<T>({
       inputType="number"
       expand={expand}
       toStr={(v?: Amount) => v?.split(":")[1] || ""}
-      fromStr={(v: string) => (!v ? "" : `${config.currency}:${v}`)}
+      fromStr={(v: string) => (!v ? undefined : `${config.currency}:${v}`)}
       inputExtra={{ min: 0 }}
     >
       {children}
diff --git 
a/packages/merchant-backoffice-ui/src/components/form/InputSelector.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputSelector.tsx
index 7a419ebb9..021977dfe 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputSelector.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputSelector.tsx
@@ -25,6 +25,7 @@ interface Props<T> extends InputProps<T> {
   readonly?: boolean;
   expand?: boolean;
   values: string[];
+  convert?: (v: string) => any;
   toStr?: (v?: any) => string;
   fromStr?: (s: string) => any;
 }
@@ -41,6 +42,7 @@ export function InputSelector<T>({
   label,
   help,
   values,
+  convert,
   toStr = defaultToString,
 }: Props<keyof T>): VNode {
   const { error, value, onChange } = useField<T>(name);
@@ -66,7 +68,10 @@ export function InputSelector<T>({
               disabled={readonly}
               readonly={readonly}
               onChange={(e) => {
-                onChange(e.currentTarget.value as any);
+                const v = convert
+                  ? convert(e.currentTarget.value)
+                  : e.currentTarget.value;
+                onChange(v);
               }}
             >
               {placeholder && <option>{placeholder}</option>}
diff --git a/packages/merchant-backoffice-ui/src/declaration.d.ts 
b/packages/merchant-backoffice-ui/src/declaration.d.ts
index c9380760c..9fc4f0d77 100644
--- a/packages/merchant-backoffice-ui/src/declaration.d.ts
+++ b/packages/merchant-backoffice-ui/src/declaration.d.ts
@@ -1287,6 +1287,9 @@ export namespace MerchantBackend {
       // This parameter is optional.
       pos_key?: string;
 
+      // Algorithm for computing the POS confirmation, 0 for none.
+      pos_algorithm?: number;
+
       // Additional information in a separate template.
       template_contract: TemplateContractDetails;
     }
@@ -1313,6 +1316,9 @@ export namespace MerchantBackend {
       // This parameter is optional.
       pos_key?: string;
 
+      // Algorithm for computing the POS confirmation, 0 for none.
+      pos_algorithm?: Integer;
+
       // Additional information in a separate template.
       template_contract: TemplateContractDetails;
     }
@@ -1338,6 +1344,9 @@ export namespace MerchantBackend {
       // This parameter is optional.
       pos_key?: string;
 
+      // Algorithm for computing the POS confirmation, 0 for none.
+      pos_algorithm?: Integer;
+
       // Additional information in a separate template.
       template_contract: TemplateContractDetails;
     }
diff --git a/packages/merchant-backoffice-ui/src/hooks/instance.ts 
b/packages/merchant-backoffice-ui/src/hooks/instance.ts
index c9eb84e74..9c76c3381 100644
--- a/packages/merchant-backoffice-ui/src/hooks/instance.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/instance.ts
@@ -223,9 +223,11 @@ export function useInstanceKYCDetails(): HttpResponse<
     HttpResponseOk<MerchantBackend.Instances.AccountKycRedirects>,
     RequestError<MerchantBackend.ErrorDetail>
   >([`/private/kyc`], fetcher, {
-    refreshInterval: 5000,
+    refreshInterval: 60 * 1000,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
+    revalidateIfStale: false,
+    revalidateOnMount: false,
     revalidateOnReconnect: false,
     refreshWhenOffline: false,
     errorRetryCount: 0,
diff --git a/packages/merchant-backoffice-ui/src/hooks/templates.ts 
b/packages/merchant-backoffice-ui/src/hooks/templates.ts
index dd096e4f9..97fb165b9 100644
--- a/packages/merchant-backoffice-ui/src/hooks/templates.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/templates.ts
@@ -244,7 +244,11 @@ export function useTemplateDetails(
   });
 
   if (isValidating) return { loading: true, data: data?.data };
-  if (data) return data;
+  if (data) {
+    const d = structuredClone(data);
+    d.data.pos_algorithm = 1;
+    return d;
+  }
   if (error) return error.info;
   return { loading: true };
 }
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
index 22f86002a..144e968c5 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
@@ -31,9 +31,11 @@ import { Input } from "../../../../components/form/Input.js";
 import { InputCurrency } from "../../../../components/form/InputCurrency.js";
 import { InputDuration } from "../../../../components/form/InputDuration.js";
 import { InputNumber } from "../../../../components/form/InputNumber.js";
+import { InputSelector } from "../../../../components/form/InputSelector.js";
 import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
 import { useBackendContext } from "../../../../context/backend.js";
 import { MerchantBackend } from "../../../../declaration.js";
+import { randomBase32Key } from "../../../../utils/crypto.js";
 import { undefinedIfEmpty } from "../../../../utils/table.js";
 
 type Entity = MerchantBackend.Template.TemplateAddDetails;
@@ -43,6 +45,13 @@ interface Props {
   onBack?: () => void;
 }
 
+const algorithms = ["0", "1", "2"];
+const algorithmsNames = [
+  "off",
+  "30s 8d TOTP-SHA1 without amount",
+  "30s 8d eTOTP-SHA1 with amount",
+];
+
 export function CreatePage({ onCreate, onBack }: Props): VNode {
   const { i18n } = useTranslationContext();
   const backend = useBackendContext();
@@ -104,7 +113,6 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
                 label={i18n.str`Identifier`}
                 tooltip={i18n.str`Name of the template in URLs.`}
               />
-
               <Input<Entity>
                 name="template_description"
                 label={i18n.str`Description`}
@@ -134,12 +142,35 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
                 help=""
                 tooltip={i18n.str`How much time has the customer to complete 
the payment once the order was created.`}
               />
-              <Input<Entity>
-                name="pos_key"
-                label={i18n.str`Point-of-sale key`}
-                help=""
-                tooltip={i18n.str`Useful to validate the purchase`}
+              <InputSelector<Entity>
+                name="pos_algorithm"
+                label={i18n.str`Veritifaction algorithm`}
+                tooltip={i18n.str`Algorithm to use to verify transaction in 
offline mode`}
+                values={algorithms}
+                toStr={(v) => algorithmsNames[v]}
+                convert={(v) => Number(v)}
               />
+              {state.pos_algorithm && state.pos_algorithm > 0 ? (
+                <Input<Entity>
+                  name="pos_key"
+                  label={i18n.str`Point-of-sale key`}
+                  help=""
+                  tooltip={i18n.str`Useful to validate the purchase`}
+                  side={
+                    <span data-tooltip={i18n.str`generate random secret key`}>
+                      <button
+                        class="button is-info mr-3"
+                        onClick={(e) => {
+                          const pos_key = randomBase32Key();
+                          setState((s) => ({ ...s, pos_key }));
+                        }}
+                      >
+                        <i18n.Translate>random</i18n.Translate>
+                      </button>
+                    </span>
+                  }
+                />
+              ) : undefined}
             </FormProvider>
 
             <div class="buttons is-right mt-5">
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
index 756909d15..66ac72ff5 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
@@ -31,8 +31,10 @@ import {
 } from "../../../../components/form/FormProvider.js";
 import { Input } from "../../../../components/form/Input.js";
 import { InputCurrency } from "../../../../components/form/InputCurrency.js";
+import { ConfirmModal } from "../../../../components/modal/index.js";
 import { useBackendContext } from "../../../../context/backend.js";
 import { useConfigContext } from "../../../../context/config.js";
+import { useInstanceContext } from "../../../../context/instance.js";
 import { MerchantBackend } from "../../../../declaration.js";
 
 type Entity = MerchantBackend.Template.UsingTemplateDetails;
@@ -46,7 +48,9 @@ interface Props {
 export function QrPage({ template, id: templateId, onBack }: Props): VNode {
   const { i18n } = useTranslationContext();
   const { url: backendUrl } = useBackendContext();
+  const { id: instanceId } = useInstanceContext();
   const config = useConfigContext();
+  const [setupTOTP, setSetupTOTP] = useState(false);
 
   const [state, setState] = useState<Partial<Entity>>({
     amount: template.template_contract.amount,
@@ -82,8 +86,33 @@ export function QrPage({ template, id: templateId, onBack }: 
Props): VNode {
 
   const payTemplateUri = 
`${talerProto}//pay-template/${merchantURL.hostname}/${templateId}${paramsStr}`;
 
+  const issuer = encodeURIComponent(
+    `${new URL(backendUrl).hostname}/${instanceId}`,
+  );
+  const oauthUri = !template.pos_algorithm
+    ? undefined
+    : template.pos_algorithm === 1
+    ? 
`otpauth://totp/${issuer}:${templateId}?secret=${template.pos_key}&issuer=${issuer}&algorithm=SHA1&digits=8&period=30`
+    : template.pos_algorithm === 2
+    ? 
`otpauth://totp/${issuer}:${templateId}?secret=${template.pos_key}&issuer=${issuer}&algorithm=SHA1&digits=8&period=30`
+    : undefined;
   return (
     <div>
+      {oauthUri && (
+        <ConfirmModal
+          description="Setup TOTP"
+          active={setupTOTP}
+          onConfirm={() => {
+            setSetupTOTP(false);
+          }}
+        >
+          <p>Scan this qr code with your TOTP device</p>
+          <QR text={oauthUri} />
+          <pre style={{ textAlign: "center" }}>
+            <a href={oauthUri}>{oauthUri}</a>
+          </pre>
+        </ConfirmModal>
+      )}
       <section class="section is-main-section">
         <div class="columns">
           <div class="column" />
@@ -114,20 +143,48 @@ export function QrPage({ template, id: templateId, onBack 
}: Props): VNode {
                   <i18n.Translate>Cancel</i18n.Translate>
                 </button>
               )}
-              <button class="button is-info" onClick={onBack}>
+              <button
+                class="button is-info"
+                onClick={() => saveAsPDF(templateId)}
+              >
                 <i18n.Translate>Print</i18n.Translate>
               </button>
+              {oauthUri && (
+                <button
+                  class="button is-info"
+                  onClick={() => setSetupTOTP(true)}
+                >
+                  <i18n.Translate>Setup TOTP</i18n.Translate>
+                </button>
+              )}
             </div>
           </div>
           <div class="column" />
         </div>
       </section>
-      <section>
-        <pre>
+      <section id="printThis">
+        <QR text={payTemplateUri} />
+        <pre style={{ textAlign: "center" }}>
           <a href={payTemplateUri}>{payTemplateUri}</a>
         </pre>
-        <QR text={payTemplateUri} />
       </section>
     </div>
   );
 }
+
+function saveAsPDF(name: string): void {
+  const printWindow = window.open("", "", "height=400,width=800");
+  if (!printWindow) return;
+  const divContents = document.getElementById("printThis");
+  if (!divContents) return;
+  printWindow.document.write(
+    `<html><head><title>Order template for ${name}</title><style>`,
+  );
+  printWindow.document.write("</style></head><body>&nbsp;</body></html>");
+  printWindow.document.close();
+  printWindow.document.body.appendChild(divContents.cloneNode(true));
+  printWindow.addEventListener("load", () => {
+    printWindow.print();
+    printWindow.close();
+  });
+}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
index 97d25b700..044cc7d79 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
@@ -51,10 +51,8 @@ export default function TemplateQrPage({
   onNotFound,
   onUnauthorized,
 }: Props): VNode {
-  const { createOrderFromTemplate } = useTemplateAPI();
   const result = useTemplateDetails(tid);
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
-  const { i18n } = useTranslationContext();
 
   if (result.clientError && result.isUnauthorized) return onUnauthorized();
   if (result.clientError && result.isNotfound) return onNotFound();
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
index eba212517..e34e2c746 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
@@ -31,9 +31,11 @@ import { Input } from "../../../../components/form/Input.js";
 import { InputCurrency } from "../../../../components/form/InputCurrency.js";
 import { InputDuration } from "../../../../components/form/InputDuration.js";
 import { InputNumber } from "../../../../components/form/InputNumber.js";
+import { InputSelector } from "../../../../components/form/InputSelector.js";
 import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
 import { useBackendContext } from "../../../../context/backend.js";
 import { MerchantBackend, WithId } from "../../../../declaration.js";
+import { randomBase32Key } from "../../../../utils/crypto.js";
 import { undefinedIfEmpty } from "../../../../utils/table.js";
 
 type Entity = MerchantBackend.Template.TemplatePatchDetails & WithId;
@@ -44,6 +46,13 @@ interface Props {
   template: Entity;
 }
 
+const algorithms = ["0", "1", "2"];
+const algorithmsNames = [
+  "off",
+  "30s 8d TOTP-SHA1 without amount",
+  "30s 8d eTOTP-SHA1 with amount",
+];
+
 export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
   const { i18n } = useTranslationContext();
   const backend = useBackendContext();
@@ -143,12 +152,35 @@ export function UpdatePage({ template, onUpdate, onBack 
}: Props): VNode {
                   help=""
                   tooltip={i18n.str`How much time has the customer to complete 
the payment once the order was created.`}
                 />
-                <Input<Entity>
-                  name="pos_key"
-                  label={i18n.str`Point-of-sale key`}
-                  help=""
-                  tooltip={i18n.str`Useful to validate the purchase`}
+                <InputSelector<Entity>
+                  name="pos_algorithm"
+                  label={i18n.str`Veritifaction algorithm`}
+                  tooltip={i18n.str`Algorithm to use to verify transaction in 
offline mode`}
+                  values={algorithms}
+                  toStr={(v) => algorithmsNames[v]}
+                  convert={(v) => Number(v)}
                 />
+                {state.pos_algorithm && state.pos_algorithm > 0 ? (
+                  <Input<Entity>
+                    name="pos_key"
+                    label={i18n.str`Point-of-sale key`}
+                    help=""
+                    tooltip={i18n.str`Useful to validate the purchase`}
+                    side={
+                      <span data-tooltip={i18n.str`generate random secret 
key`}>
+                        <button
+                          class="button is-info mr-3"
+                          onClick={(e) => {
+                            const pos_key = randomBase32Key();
+                            setState((s) => ({ ...s, pos_key }));
+                          }}
+                        >
+                          <i18n.Translate>random</i18n.Translate>
+                        </button>
+                      </span>
+                    }
+                  />
+                ) : undefined}
               </FormProvider>
 
               <div class="buttons is-right mt-5">
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx
index a63469763..5abc6b153 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx
@@ -34,12 +34,13 @@ import { MerchantBackend } from 
"../../../../declaration.js";
 type Entity = MerchantBackend.Template.UsingTemplateDetails;
 
 interface Props {
+  id: string;
   template: MerchantBackend.Template.TemplateDetails;
   onCreateOrder: (d: Entity) => Promise<void>;
   onBack?: () => void;
 }
 
-export function UsePage({ template, onCreateOrder, onBack }: Props): VNode {
+export function UsePage({ id, template, onCreateOrder, onBack }: Props): VNode 
{
   const { i18n } = useTranslationContext();
 
   const [state, setState] = useState<Partial<Entity>>({
@@ -75,6 +76,22 @@ export function UsePage({ template, onCreateOrder, onBack }: 
Props): VNode {
 
   return (
     <div>
+      <section class="section">
+        <section class="hero is-hero-bar">
+          <div class="hero-body">
+            <div class="level">
+              <div class="level-left">
+                <div class="level-item">
+                  <span class="is-size-4">
+                    <i18n.Translate>New order for template</i18n.Translate>:{" 
"}
+                    <b>{id}</b>
+                  </span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </section>
+      </section>
       <section class="section is-main-section">
         <div class="columns">
           <div class="column" />
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx
index d5fa6d39d..b6175bcfb 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx
@@ -68,6 +68,7 @@ export default function TemplateUsePage({
       <NotificationCard notification={notif} />
       <UsePage
         template={result.data}
+        id={tid}
         onBack={onBack}
         onCreateOrder={(
           request: MerchantBackend.Template.UsingTemplateDetails,
diff --git a/packages/merchant-backoffice-ui/src/scss/_form.scss 
b/packages/merchant-backoffice-ui/src/utils/crypto.ts
similarity index 50%
copy from packages/merchant-backoffice-ui/src/scss/_form.scss
copy to packages/merchant-backoffice-ui/src/utils/crypto.ts
index bd28a17cf..7bab8abf1 100644
--- a/packages/merchant-backoffice-ui/src/scss/_form.scss
+++ b/packages/merchant-backoffice-ui/src/utils/crypto.ts
@@ -19,53 +19,35 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-.field {
-  &.has-check {
-    .field-body {
-      margin-top: $default-padding * 0.125;
+const encTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+// base32 RFC 3548
+function encodeBase32(data: ArrayBuffer) {
+  const dataBytes = new Uint8Array(data);
+  let sb = "";
+  const size = data.byteLength;
+  let bitBuf = 0;
+  let numBits = 0;
+  let pos = 0;
+  while (pos < size || numBits > 0) {
+    if (pos < size && numBits < 5) {
+      const d = dataBytes[pos++];
+      bitBuf = (bitBuf << 8) | d;
+      numBits += 8;
     }
-  }
-  .control {
-    .mdi-24px.mdi-set,
-    .mdi-24px.mdi:before {
-      font-size: inherit;
-    }
-  }
-}
-.upload {
-  .upload-draggable {
-    display: block;
-  }
-}
-
-.input,
-.textarea,
-select {
-  box-shadow: none;
-
-  &:focus,
-  &:active {
-    box-shadow: none !important;
-  }
-}
-
-.switch input[type="checkbox"] + .check:before {
-  box-shadow: none;
-}
-
-.switch,
-.b-checkbox.checkbox {
-  input[type="checkbox"] {
-    &:focus + .check,
-    &:focus:checked + .check {
-      box-shadow: none !important;
+    if (numBits < 5) {
+      // zero-padding
+      bitBuf = bitBuf << (5 - numBits);
+      numBits = 5;
     }
+    const v = (bitBuf >>> (numBits - 5)) & 31;
+    sb += encTable[v];
+    numBits -= 5;
   }
+  return sb;
 }
 
-.b-checkbox.checkbox input[type="checkbox"],
-.b-radio.radio input[type="radio"] {
-  & + .check {
-    border: $checkbox-border;
-  }
+export function randomBase32Key(): string {
+  var buf = new Uint8Array(20);
+  window.crypto.getRandomValues(buf);
+  return encodeBase32(buf);
 }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]