aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/context/alert.ts
blob: cc98ec1e0112228a88caf910ec02d2e808fb0e50 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/*
 This file is part of GNU Taler
 (C) 2022 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 { TranslatedString } from "@gnu-taler/taler-util";
import { ComponentChildren, createContext, h, VNode } from "preact";
import { useContext, useState } from "preact/hooks";

export type AlertType = "info" | "warning" | "error" | "success";

export interface Alert {
  message: TranslatedString;
  description: TranslatedString | VNode;
  type: AlertType;
}

export interface ErrorAlert extends Alert {
  type: "error";
  context: object;
  cause: any;
}

type Type = {
  alerts: Alert[];
  pushAlert: (n: Alert) => void;
  removeAlert: (n: Alert) => void;
};

const initial: Type = {
  alerts: [],
  pushAlert: () => {
    null;
  },
  removeAlert: () => {
    null;
  },
};

const Context = createContext<Type>(initial);

type AlertWithDate = Alert & { since: Date };

type Props = Partial<Type> & {
  children: ComponentChildren;
};

export const AlertProvider = ({ children }: Props): VNode => {
  const timeout = 3000;

  const [alerts, setAlerts] = useState<AlertWithDate[]>([]);

  const pushAlert = (n: Alert): void => {
    const entry = { ...n, since: new Date() };
    setAlerts((ns) => [...ns, entry]);
    if (n.type !== "error") {
      setTimeout(() => {
        setAlerts((ns) => ns.filter((x) => x.since !== entry.since));
      }, timeout);
    }
  };

  const removeAlert = (alert: Alert): void => {
    setAlerts((ns: AlertWithDate[]) => ns.filter((n) => n !== alert));
  };

  return h(Context.Provider, {
    value: { alerts, pushAlert, removeAlert },
    children,
  });
};

export const useAlertContext = (): Type => useContext(Context);

export function alertFromError(
  message: TranslatedString,
  error: unknown,
  ...context: any[]
): ErrorAlert {
  let description = "" as TranslatedString;

  const isObject = typeof error === "object" &&
    error !== null;
  const hasMessage =
    isObject &&
    "message" in error &&
    typeof error.message === "string";

  if (hasMessage) {
    description = error.message as TranslatedString;
  } else {
    description = `Unknown error: ${String(error)}` as TranslatedString;
  }

  return {
    type: "error",
    message,
    description,
    cause: error,
    context,
  };
}