Merge branch 'main' into 19836-new-tooltip-impl
[arvados-workbench2.git] / src / store / tooltips / tooltips-middleware.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { CollectionDirectory, CollectionFile } from "models/collection-file";
6 import { Middleware, Store } from "redux";
7 import { ServiceRepository } from "services/services";
8 import { RootState } from "store/store";
9 import tippy, { createSingleton } from 'tippy.js';
10 import 'tippy.js/dist/tippy.css';
11
12 let running = false;
13 let tooltipsContents = null;
14 let tooltipsFetchFailed = false;
15 export const TOOLTIP_LOCAL_STORAGE_KEY = "TOOLTIP_LOCAL_STORAGE_KEY";
16
17 const tippySingleton = createSingleton([], {delay: 10});
18
19 export const tooltipsMiddleware = (services: ServiceRepository): Middleware => (store: Store) => next => action => {
20     const state: RootState = store.getState();
21     const hideTooltip = localStorage.getItem(TOOLTIP_LOCAL_STORAGE_KEY);
22     const { BannerUUID } = (state.auth.config.clusterConfig.Workbench as any);
23
24     const bannerUUID = BannerUUID || 'tordo-4zz18-1buneu6sb8zxiti';
25
26     if (bannerUUID && !tooltipsContents && !hideTooltip && !tooltipsFetchFailed && !running) {
27         running = true;
28         fetchTooltips(services, bannerUUID);
29     } else if (tooltipsContents && !hideTooltip && !tooltipsFetchFailed) {
30         applyTooltips();
31     }
32
33     return next(action);
34 };
35
36 const fetchTooltips = (services, bannerUUID) => {
37     services.collectionService.files(bannerUUID)
38         .then(results => {
39             const tooltipsFile: CollectionDirectory | CollectionFile | undefined = results.find(({ name }) => name === 'tooltips.json');
40
41             if (tooltipsFile) {
42                 running = true;
43                 services.collectionService.getFileContents(tooltipsFile as CollectionFile)
44                     .then(data => {
45                         tooltipsContents = JSON.parse(data);
46                         applyTooltips();
47                     })
48                     .catch(() => {})
49                     .finally(() => {
50                         running = false;
51                     });
52             }  else {
53                 tooltipsFetchFailed = true;
54             }
55         })
56         .catch(() => {})
57         .finally(() => {
58             running = false;
59         });
60 };
61
62 const applyTooltips = () => {
63     const tippyInstances: any[] = Object.keys(tooltipsContents as any)
64         .map((key) => {
65             const content = (tooltipsContents as any)[key]
66             const element = document.querySelector(key);
67
68             if (element) {
69                 const hasTippyAttatched = !!(element as any)._tippy;
70
71                 if (!hasTippyAttatched && tooltipsContents) {
72                     return tippy(element as any, { content });
73                 }
74             }
75
76             return null;
77         })
78         .filter(data => !!data);
79
80     if (tippyInstances.length > 0) {
81         tippySingleton.setInstances(tippyInstances);
82     }
83 };