Merge branch '21128-toolbar-context-menu'
[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
22     if (state && state.auth && state.auth.config && state.auth.config.clusterConfig && state.auth.config.clusterConfig.Workbench) {
23         const hideTooltip = localStorage.getItem(TOOLTIP_LOCAL_STORAGE_KEY);
24         const { BannerUUID: bannerUUID } = state.auth.config.clusterConfig.Workbench;
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
34     return next(action);
35 };
36
37 const fetchTooltips = (services, bannerUUID) => {
38     services.collectionService.files(bannerUUID)
39         .then(results => {
40             const tooltipsFile: CollectionDirectory | CollectionFile | undefined = results.find(({ name }) => name === 'tooltips.json');
41
42             if (tooltipsFile) {
43                 running = true;
44                 services.collectionService.getFileContents(tooltipsFile as CollectionFile)
45                     .then(data => {
46                         tooltipsContents = JSON.parse(data);
47                         applyTooltips();
48                     })
49                     .catch(() => {})
50                     .finally(() => {
51                         running = false;
52                     });
53             }  else {
54                 tooltipsFetchFailed = true;
55             }
56         })
57         .catch(() => {})
58         .finally(() => {
59             running = false;
60         });
61 };
62
63 const applyTooltips = () => {
64     const tippyInstances: any[] = Object.keys(tooltipsContents as any)
65         .map((key) => {
66             const content = (tooltipsContents as any)[key]
67             const element = document.querySelector(key);
68
69             if (element) {
70                 const hasTippyAttatched = !!(element as any)._tippy;
71
72                 if (!hasTippyAttatched && tooltipsContents) {
73                     return tippy(element as any, { content });
74                 }
75             }
76
77             return null;
78         })
79         .filter(data => !!data);
80
81     if (tippyInstances.length > 0) {
82         tippySingleton.setInstances(tippyInstances);
83     }
84 };