Merge branch 'main' into 15397-remove-obsolete-apis
[arvados.git] / services / workbench2 / src / views-components / baner / banner.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import React, { useState, useCallback, useEffect } from "react";
6 import { Dialog, DialogContent, DialogActions, Button, StyleRulesCallback, withStyles, WithStyles } from "@material-ui/core";
7 import { connect } from "react-redux";
8 import { RootState } from "store/store";
9 import bannerActions from "store/banner/banner-action";
10 import { ArvadosTheme } from "common/custom-theme";
11 import servicesProvider from "common/service-provider";
12 import { Dispatch } from "redux";
13 import { sanitizeHTML } from "common/html-sanitize";
14
15 type CssRules = "dialogContent" | "dialogContentIframe";
16
17 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
18     dialogContent: {
19         minWidth: "550px",
20         minHeight: "500px",
21         display: "block",
22     },
23     dialogContentIframe: {
24         minWidth: "550px",
25         minHeight: "500px",
26     },
27 });
28
29 interface BannerProps {
30     isOpen: boolean;
31     bannerUUID?: string;
32     keepWebInlineServiceUrl: string;
33 }
34
35 type BannerComponentProps = BannerProps &
36     WithStyles<CssRules> & {
37         openBanner: Function;
38         closeBanner: Function;
39     };
40
41 const mapStateToProps = (state: RootState): BannerProps => ({
42     isOpen: state.banner.isOpen,
43     bannerUUID: state.auth.config.clusterConfig.Workbench.BannerUUID,
44     keepWebInlineServiceUrl: state.auth.config.keepWebInlineServiceUrl,
45 });
46
47 const mapDispatchToProps = (dispatch: Dispatch) => ({
48     openBanner: () => dispatch<any>(bannerActions.openBanner()),
49     closeBanner: () => dispatch<any>(bannerActions.closeBanner()),
50 });
51
52 export const BANNER_LOCAL_STORAGE_KEY = "bannerFileData";
53
54 export const BannerComponent = (props: BannerComponentProps) => {
55     const { isOpen, openBanner, closeBanner, bannerUUID, keepWebInlineServiceUrl } = props;
56     const [bannerContents, setBannerContents] = useState(`<h1>Loading ...</h1>`);
57
58     const onConfirm = useCallback(() => {
59         closeBanner();
60     }, [closeBanner]);
61
62     useEffect(() => {
63         if (!!bannerUUID && bannerUUID !== "") {
64             servicesProvider
65                 .getServices()
66                 .collectionService.files(bannerUUID)
67                 .then(results => {
68                     const bannerFileData = results.find(({ name }) => name === "banner.html");
69                     const result = localStorage.getItem(BANNER_LOCAL_STORAGE_KEY);
70
71                     if (result && result === JSON.stringify(bannerFileData) && !isOpen) {
72                         return;
73                     }
74
75                     if (bannerFileData) {
76                         servicesProvider
77                             .getServices()
78                             .collectionService.getFileContents(bannerFileData)
79                             .then(data => {
80                                 setBannerContents(data);
81                                 openBanner();
82                                 localStorage.setItem(BANNER_LOCAL_STORAGE_KEY, JSON.stringify(bannerFileData));
83                             });
84                     }
85                 }).catch((e) => {
86                     console.error("Failed to load banner", e);
87                 });
88         }
89     }, [bannerUUID, keepWebInlineServiceUrl, openBanner, isOpen]);
90
91     return (
92         <Dialog
93             open={isOpen}
94             maxWidth="md"
95         >
96             <div data-cy="confirmation-dialog">
97                 <DialogContent className={props.classes.dialogContent}>
98                     <div dangerouslySetInnerHTML={{ __html: sanitizeHTML(bannerContents) }}></div>
99                 </DialogContent>
100                 <DialogActions style={{ margin: "0px 24px 24px" }}>
101                     <Button
102                         data-cy="confirmation-dialog-ok-btn"
103                         variant="contained"
104                         color="primary"
105                         type="submit"
106                         onClick={onConfirm}
107                     >
108                         Close
109                     </Button>
110                 </DialogActions>
111             </div>
112         </Dialog>
113     );
114 };
115
116 export const Banner = withStyles(styles)(connect(mapStateToProps, mapDispatchToProps)(BannerComponent));