{"version":3,"file":"index.modern.mjs","sources":["../src/AbstractDecafNativeController.ts","../src/context.tsx","../src/DecafVersionChecker.tsx","../src/DecafSpinner.tsx","../src/DecafWebappController.tsx","../src/OfflineChecker.tsx","../src/ZendeskWidget.tsx","../src/DecafApp.tsx"],"sourcesContent":["import { DecafClient } from '@decafhub/decaf-client';\nimport { ReactNode } from 'react';\nimport { DecafAppController, RedirectReason } from './DecafAppController';\n\nexport abstract class AbstractDecafNativeController implements DecafAppController {\n  private client: DecafClient;\n\n  constructor(client: DecafClient) {\n    this.client = client;\n  }\n\n  getDecafClient() {\n    return this.client;\n  }\n\n  abstract onInvalidSession(reason: RedirectReason): null;\n\n  abstract onLoadingState(loading: boolean): null;\n\n  abstract loadingComponent: ReactNode;\n}\n","import { DecafClient } from '@decafhub/decaf-client';\nimport React, { JSX, useContext } from 'react';\nimport { DecafAppController } from './DecafAppController';\n\nexport const DecafContext = React.createContext<DecafContextType>({\n  client: undefined as unknown as DecafClient,\n  me: undefined as unknown as Principal,\n  publicConfig: undefined as unknown as PublicConfig,\n  privateConfig: undefined as unknown as PrivateConfig,\n  controller: undefined as unknown as DecafAppController,\n});\n\nexport function DecafProvider({ children, value }: { children: JSX.Element; value: DecafContextType }) {\n  return <DecafContext.Provider value={value}>{children}</DecafContext.Provider>;\n}\n\nexport const useDecaf = () => useContext(DecafContext);\n\n/// ///////////////////////\n// INTERNAL DEFINITIONS //\n/// ///////////////////////\n\nexport interface DecafContextType {\n  client: DecafClient;\n  me: Principal;\n  publicConfig: PublicConfig;\n  privateConfig: PrivateConfig;\n  controller: DecafAppController;\n}\n\nexport interface Role {\n  code: string;\n  name: string;\n}\n\nexport interface Team {\n  id: number;\n  name: string;\n}\n\nexport interface Principal {\n  id: number;\n  guid: string;\n  username: string;\n  fullname: string;\n  first_name: string;\n  last_name: string;\n  email?: string;\n  mobile?: string;\n  active: boolean;\n  roles: Role[];\n  teams: Team[];\n  internal: boolean;\n  external: boolean;\n  privileged: boolean;\n}\n\nexport interface PublicConfig {\n  /** company short name */\n  shortname: string;\n  /** company full name */\n  legalname: string;\n  /** company web site */\n  website: string;\n  /** logo url */\n  logo: string;\n  /** terms and conditions */\n  tnc: string;\n  /** zendeks code */\n  zendesk?: string;\n  /** google analytics code */\n  googleax?: string;\n  /** one-time password feature? */\n  otp: null;\n  /** password reset feature should be enabled or not */\n  pwdreset: true;\n}\n\n/**\n * Type encoding for global private deployment configuration.\n *\n * This configuration value is instantiated during runtime from\n * `/api/conf/private/` API endpoint.\n */\nexport interface PrivateConfig {\n  functions: {\n    portmngt: boolean;\n    fundmngt: boolean;\n    ebanking: boolean;\n    pretrade: boolean;\n  };\n  beanbag?: string;\n  releasenotes: string;\n  documentation: Resource[];\n}\n\n/**\n * Type encoding for a single documentation item.\n */\nexport interface Resource {\n  title: string;\n  description: string;\n  role: 'privileged' | 'internal' | '*';\n  authed: boolean;\n  document: { type: 'internal' | 'external'; link: string };\n}\n","import React, { useEffect, useRef, useState } from 'react';\n\nconst style = `\n.version-modal {\n  background-color: rgba(0, 0, 0, 0.5);\n  color: white;\n  position: fixed;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  z-index: 9999;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  font-size: 1.4em;\n}\n\n.version-modal-title {\n  text-align: center;\n  padding: 10px 20px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  color: white;\n}\n\n.version-modal-title h2 {\n  font-size: 1.5rem;\n  font-weight: bold;\n  color: white;\n  margin: 0;\n  margin-left: 10px;\n}\n\n.version-modal .version-modal-body {\n  display: flex;\n  flex-direction: column;\n  width: 50%;\n  background-color: #333;\n  border-radius: 5px;\n  color: white;\n}\n\n.version-modal .version-modal-content {\n  padding: 5px 20px;\n  color: white;\n}\n\n.version-modal .version-modal-footer {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  width: 100%;\n  padding: 10px;\n  margin-top: 20px;\n}\n.version-modal .reload-btn {\n  background-color: #177ddc;\n  color: white;\n  border: none;\n  width: 150px;\n  padding: 10px;\n  cursor: pointer;\n}\n\n.version-modal .reload-btn:hover {\n  background-color: #095cb5;\n}\n\n.version-modal .cancel-btn {\n  background-color: #555;\n  color: white;\n  border: 1px solid #dfdfdf;\n  padding: 10px;\n  cursor: pointer;\n}\n\n.version-modal .cancel-btn:hover {\n  background-color: #444;\n}\n`;\n\nexport interface DecafVersionCheckerProps {\n  currentVersion: string;\n  onNewVersion?: (versionOld: string, versionNew: string) => void;\n  basePath?: string;\n  interval?: number;\n}\n\nexport default function DecafVersionChecker(props: DecafVersionCheckerProps) {\n  const [newVersion, setNewVersion] = useState();\n  const interval = useRef<number>(undefined);\n\n  useEffect(() => {\n    if (interval.current) {\n      clearInterval(interval.current);\n    }\n    interval.current = window.setInterval(\n      () => {\n        fetch((props.basePath ?? '') + '/version.json?t=' + new Date().getTime())\n          .then((res) => res.json())\n          .then((data) => {\n            if (data.version) {\n              setNewVersion(data.version);\n              if (props.currentVersion !== data.version) {\n                props.onNewVersion?.(props.currentVersion, data.version);\n              }\n            }\n          })\n          .catch(() => {\n            console.error('DECAF Error: Can not fetch version information!');\n          });\n      },\n      (props.interval || 60) * 1000\n    );\n    return () => {\n      clearInterval(interval.current);\n    };\n  }, [props]);\n\n  if (props.onNewVersion || !newVersion) {\n    return null;\n  }\n\n  return (\n    <div>\n      {newVersion !== props.currentVersion && (\n        <div className=\"version-modal\">\n          <style>{style}</style>\n          <div className=\"version-modal-body\">\n            <div className=\"version-modal-title\">\n              <svg\n                xmlns=\"http://www.w3.org/2000/svg\"\n                width=\"50\"\n                height=\"50\"\n                viewBox=\"0 0 24 24\"\n                fill={'#00c12c'}\n                className=\"alert-status-icon\"\n              >\n                {\n                  <path d=\"M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm6.25 8.891l-1.421-1.409-6.105 6.218-3.078-2.937-1.396 1.436 4.5 4.319 7.5-7.627z\" />\n                }\n              </svg>\n              <h2>New Version Available</h2>\n            </div>\n            <div className=\"version-modal-content\">\n              <p>A new version of your app is available. Please reload the page to update to the latest version.</p>\n              <span>\n                New version: <b style={{ marginRight: 10 }}>{newVersion}</b>\n                (Current version: <b>{props.currentVersion}</b>)\n              </span>\n            </div>\n            <div className=\"version-modal-footer\">\n              <button className=\"cancel-btn\" onClick={() => setNewVersion(undefined)}>\n                I will reload later\n              </button>\n              <button\n                className=\"reload-btn\"\n                onClick={() => {\n                  window.location.reload();\n                }}\n              >\n                Reload\n              </button>\n            </div>\n          </div>\n        </div>\n      )}\n    </div>\n  );\n}\n","import React from 'react';\n\nexport type DecafSpinnerType = {\n  title?: string;\n  color?: string;\n  size?: number;\n  titleColor?: string;\n};\n/**\n *\n * @param title string\n * @param color string\n * @param size number\n * @param titleColor string\n * @returns JSX.Element\n */\nexport default function DecafSpinner(props: DecafSpinnerType) {\n  const style = `\n    .spinner-wrapper {\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      height: 100vh;\n      flex-direction: column;\n    }\n    .spinner-text {\n      color: ${props.titleColor || '#255d91'};\n    }\n    .spinner {\n      width: ${props.size || '40px'};\n      height: ${props.size || '40px'};\n      position: relative;\n      margin: 10px auto;\n    }\n\n    .double-bounce1,\n    .double-bounce2 {\n      width: 100%;\n      height: 100%;\n      border-radius: 50%;\n      background-color: ${props.color || '#1890ff'};\n      opacity: 0.6;\n      position: absolute;\n      top: 0;\n      left: 0;\n\n      -webkit-animation: sk-bounce 2s infinite ease-in-out;\n      animation: sk-bounce 2s infinite ease-in-out;\n    }\n\n    .double-bounce2 {\n      -webkit-animation-delay: -1s;\n      animation-delay: -1s;\n    }\n\n    @-webkit-keyframes sk-bounce {\n      0%,\n      100% {\n        -webkit-transform: scale(0);\n      }\n      50% {\n        -webkit-transform: scale(1);\n      }\n    }\n\n    @keyframes sk-bounce {\n      0%,\n      100% {\n        transform: scale(0);\n        -webkit-transform: scale(0);\n      }\n      50% {\n        transform: scale(1);\n        -webkit-transform: scale(1);\n      }\n    }\n  `;\n  return (\n    <div className=\"spinner-wrapper\">\n      <style>{style}</style>\n      <div className=\"spinner\">\n        <div className=\"double-bounce1\"></div>\n        <div className=\"double-bounce2\"></div>\n      </div>\n      {props.title && <p className=\"spinner-text\">{props.title}</p>}\n    </div>\n  );\n}\n","import { buildDecafClient } from '@decafhub/decaf-client';\nimport React from 'react';\nimport { DecafAppController } from './DecafAppController';\nimport DecafSpinner from './DecafSpinner';\n\nexport function getCookie(name: string): string | undefined {\n  // Check if we are on Browser environment:\n  if (typeof document === 'undefined') {\n    // Nope! Raise an exception:\n    throw new Error('Can not read cookie: Not on browser environment!');\n  }\n\n  // Attempt to get the cookie:\n  const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));\n\n  // Return the value of the cookie if found, `undefined` otherwise.\n  return match ? decodeURIComponent(match[2]) : undefined;\n}\n\nexport function getAuthenticationToken(): string | undefined {\n  // Attempt to get the cookie value:\n  const cookie = getCookie('ember_simple_auth-session');\n\n  // If no cookie, return nothing:\n  if (!cookie) {\n    return undefined;\n  }\n\n  try {\n    // Attempt to parse the cookie value:\n    const authinfo = JSON.parse(cookie);\n\n    // Get the token, if any:\n    const token: string | undefined = authinfo?.authenticated?.token;\n\n    // Done, return the token:\n    return token;\n  } catch {\n    console.error('Can not parse authentication information!');\n    return undefined;\n  }\n}\n\nexport const DecafWebappController: DecafAppController = {\n  getDecafClient() {\n    // Attempt to get the authentication token:\n    const token = getAuthenticationToken();\n\n    // Check token, build client and return:\n    return token ? buildDecafClient('', { token }) : undefined;\n  },\n\n  onInvalidSession(reason) {\n    window.location.href = `/webapps/waitress/production/?next=${window.location.href}&reason=${reason}`;\n    return null;\n  },\n\n  onLoadingState(_loading: boolean) {\n    return null;\n  },\n\n  loadingComponent: <DecafSpinner title=\"Please Wait...\" />,\n};\n","import React, { useEffect, useRef, useState } from 'react';\n\nconst DECAF_API_VERSION_PATH = '/api/version/';\n\nexport type ConnectionStatus = 'online' | 'slow' | 'offline';\n\nexport interface UseConnectionStatusOptions {\n  /** Poll interval in ms. Default 10000. */\n  interval?: number;\n  /** A successful probe slower than this counts as a slow probe. Default 8000. */\n  slowThreshold?: number;\n  /** Hard abort timeout for a probe. Default 12000. Aborts are treated as slow, not offline. */\n  hardTimeout?: number;\n  /** Consecutive network-error probes required before flipping to 'offline'. Default 3. */\n  offlineAfter?: number;\n  /** Consecutive slow probes required before flipping to 'slow'. Default 2. */\n  slowAfter?: number;\n}\n\nconst DEFAULTS: Required<UseConnectionStatusOptions> = {\n  interval: 10000,\n  slowThreshold: 8000,\n  hardTimeout: 12000,\n  offlineAfter: 3,\n  slowAfter: 2,\n};\n\n/**\n * Probes a URL on an interval and reports a three-state connection status.\n *\n * Detection rules:\n *  - A probe that resolves within `slowThreshold` ms → resets all counters, status `online`.\n *  - A probe that resolves but exceeded `slowThreshold`, or aborted at `hardTimeout` → counts as slow.\n *    Status flips to `slow` only after `slowAfter` consecutive slow probes.\n *  - A probe rejected with a real network error (not an abort) → counts as a network failure.\n *    Status flips to `offline` only after `offlineAfter` consecutive network failures.\n *  - `navigator.onLine === false` (or the `offline` window event) is trusted as an immediate\n *    `offline` signal; the `online` event clears it back to `online`.\n *  - Polling pauses while the tab is hidden to avoid accumulating failures from a throttled tab.\n */\nexport function useConnectionStatus(url: string, options: UseConnectionStatusOptions = {}): ConnectionStatus {\n  // Use nullish coalescing per field: `{...DEFAULTS, ...options}` would let an explicit `undefined`\n  // (common when callers spread optional props through) overwrite a default and break timers.\n  const interval = options.interval ?? DEFAULTS.interval;\n  const slowThreshold = options.slowThreshold ?? DEFAULTS.slowThreshold;\n  const hardTimeout = options.hardTimeout ?? DEFAULTS.hardTimeout;\n  const offlineAfter = options.offlineAfter ?? DEFAULTS.offlineAfter;\n  const slowAfter = options.slowAfter ?? DEFAULTS.slowAfter;\n\n  const [status, setStatus] = useState<ConnectionStatus>(() =>\n    typeof navigator !== 'undefined' && navigator.onLine === false ? 'offline' : 'online'\n  );\n\n  // Counters live in refs so probe outcomes never trigger a re-render unless the status changes.\n  const slowCount = useRef(0);\n  const errorCount = useRef(0);\n\n  useEffect(() => {\n    let cancelled = false;\n    let nextTimer: ReturnType<typeof setTimeout> | undefined;\n\n    // Probes are chained (next one scheduled when the previous settles) instead of running\n    // on a fixed interval. With a 12s hardTimeout and 10s interval, setInterval would let\n    // probes overlap and complete out-of-order, corrupting slowCount/errorCount.\n    const runProbe = () => {\n      if (cancelled) return;\n      if (document.visibilityState === 'hidden') {\n        nextTimer = setTimeout(runProbe, interval);\n        return;\n      }\n\n      const controller = new AbortController();\n      const startedAt = Date.now();\n      const abortTimer = setTimeout(() => controller.abort(), hardTimeout);\n\n      fetch(`${url}?t=${startedAt}`, {\n        method: 'HEAD',\n        mode: 'no-cors',\n        cache: 'no-cache',\n        signal: controller.signal,\n      })\n        .then(() => {\n          if (cancelled) return;\n          const elapsed = Date.now() - startedAt;\n          if (elapsed > slowThreshold) {\n            // The probe actually resolved, so we know we're reachable — clear offline if set.\n            errorCount.current = 0;\n            slowCount.current += 1;\n            if (slowCount.current >= slowAfter) {\n              setStatus('slow');\n            }\n          } else {\n            slowCount.current = 0;\n            errorCount.current = 0;\n            setStatus('online');\n          }\n        })\n        .catch((err: unknown) => {\n          if (cancelled) return;\n          const isAbort = err instanceof DOMException && err.name === 'AbortError';\n          if (isAbort) {\n            // Aborted by our hard timeout: treat as slow, never as offline. We do NOT\n            // downgrade an existing offline state here — an abort doesn't prove reachability.\n            errorCount.current = 0;\n            slowCount.current += 1;\n            if (slowCount.current >= slowAfter) {\n              setStatus((prev) => (prev === 'offline' ? prev : 'slow'));\n            }\n          } else {\n            // Real network failure (e.g. TypeError \"Failed to fetch\").\n            errorCount.current += 1;\n            if (errorCount.current >= offlineAfter) {\n              setStatus('offline');\n            }\n          }\n        })\n        .finally(() => {\n          clearTimeout(abortTimer);\n          if (!cancelled) nextTimer = setTimeout(runProbe, interval);\n        });\n    };\n\n    nextTimer = setTimeout(runProbe, interval);\n\n    return () => {\n      cancelled = true;\n      if (nextTimer) clearTimeout(nextTimer);\n    };\n  }, [url, interval, slowThreshold, hardTimeout, offlineAfter, slowAfter]);\n\n  // Trust the browser's own offline signal immediately; don't wait for probes.\n  useEffect(() => {\n    const goOnline = () => {\n      slowCount.current = 0;\n      errorCount.current = 0;\n      setStatus('online');\n    };\n    const goOffline = () => setStatus('offline');\n\n    window.addEventListener('online', goOnline);\n    window.addEventListener('offline', goOffline);\n\n    return () => {\n      window.removeEventListener('online', goOnline);\n      window.removeEventListener('offline', goOffline);\n    };\n  }, []);\n\n  return status;\n}\n\n/**\n * Backward-compatible boolean hook. `true` means not offline (online or slow).\n * @deprecated Prefer `useConnectionStatus` for finer-grained state.\n */\nexport function useOnlineStatus(url: string, interval: number = 10000): boolean {\n  return useConnectionStatus(url, { interval }) !== 'offline';\n}\n\nconst styles = {\n  offline_pill: {\n    position: 'fixed' as const,\n    bottom: 40,\n    right: 10,\n    display: 'flex',\n    alignItems: 'center',\n    gap: 6,\n    padding: '4px 10px',\n    borderRadius: 12,\n    backgroundColor: 'rgba(220, 38, 38, 0.12)',\n    border: '1px solid rgba(220, 38, 38, 0.5)',\n    color: '#b91c1c',\n    fontSize: 12,\n    fontWeight: 500,\n    lineHeight: 1,\n    zIndex: 9999,\n  },\n  offline_icon: {\n    fill: '#b91c1c',\n  },\n  slow_dot: {\n    position: 'fixed' as const,\n    bottom: 44,\n    right: 14,\n    width: 10,\n    height: 10,\n    borderRadius: '50%',\n    backgroundColor: '#f59e0b',\n    boxShadow: '0 0 0 2px rgba(245, 158, 11, 0.2)',\n    zIndex: 9999,\n  },\n};\n\nexport interface OfflineNotifierProps {\n  url?: string;\n  /** Show a subtle indicator on sustained slow connections. Default true. */\n  showSlowIndicator?: boolean;\n  /** Probe interval in ms. Default 10000. */\n  interval?: number;\n  /** Threshold above which a successful probe is considered slow. Default 8000. */\n  slowThreshold?: number;\n  /** Hard probe timeout. Default 12000. */\n  hardTimeout?: number;\n  /** Consecutive network errors before showing the offline pill. Default 3. */\n  offlineAfter?: number;\n  /** Consecutive slow probes before showing the slow indicator. Default 2. */\n  slowAfter?: number;\n}\n\n/**\n * Renders a connection status indicator at the bottom-right.\n *  - Online: nothing.\n *  - Slow (rare, by design): a small amber dot with a tooltip.\n *  - Offline: a subtle red pill.\n */\nexport function OfflineNotifier({\n  url = DECAF_API_VERSION_PATH,\n  showSlowIndicator = true,\n  interval,\n  slowThreshold,\n  hardTimeout,\n  offlineAfter,\n  slowAfter,\n}: OfflineNotifierProps) {\n  const status = useConnectionStatus(url, {\n    interval,\n    slowThreshold,\n    hardTimeout,\n    offlineAfter,\n    slowAfter,\n  });\n\n  if (status === 'online') return null;\n\n  if (status === 'slow') {\n    if (!showSlowIndicator) return null;\n    return <div style={styles.slow_dot} title=\"Slow connection\" aria-label=\"Slow connection\" />;\n  }\n\n  return (\n    <div style={styles.offline_pill} role=\"status\" aria-live=\"polite\">\n      <svg\n        style={styles.offline_icon}\n        height=\"14\"\n        viewBox=\"0 0 32 32\"\n        width=\"14\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        aria-hidden=\"true\"\n      >\n        <path d=\"M24.8008,12.1362a8.8694,8.8694,0,0,0-.9795-2.5434L30,3.4142,28.5872,2,2,28.5872,3.4142,30l5-5H23.5a6.4974,6.4974,0,0,0,1.3008-12.8638ZM23.5,23H10.4141L22.3418,11.0723a6.9049,6.9049,0,0,1,.6006,2.0708l.0986.812.8154.0639A4.4975,4.4975,0,0,1,23.5,23Z\" />\n        <path d=\"M4.2964,23.4487l1.4313-1.4311A4.4774,4.4774,0,0,1,8.144,14.019l.8155-.0639.0991-.812a6.9867,6.9867,0,0,1,10.63-5.0865l1.4431-1.4428A8.9859,8.9859,0,0,0,7.2,12.1362,6.4891,6.4891,0,0,0,4.2964,23.4487Z\" />\n      </svg>\n      <span>Offline</span>\n    </div>\n  );\n}\n","import { useEffect } from 'react';\n\nexport interface ZendeskWidgetProps {\n  /** public zendesk key */\n  zendeskKey: string;\n  /** zendesk widget settings. see:\n   *\n   * https://developer.zendesk.com/api-reference/widget/settings/\n   * */\n  settings?: Record<string, any>;\n}\n\nconst ZENDESK_WIDGET_SCRIPT = 'https://static.zdassets.com/ekr/snippet.js';\n\nexport default function ZendeskWidget(props: ZendeskWidgetProps) {\n  useEffect(() => {\n    if (!props.zendeskKey || typeof document === 'undefined') return;\n    const script = document.createElement('script');\n    script.src = ZENDESK_WIDGET_SCRIPT + '?key=' + props.zendeskKey;\n    script.async = true;\n    script.id = 'ze-snippet'; // do not change this. zendesk expects this to be ze-snippet\n    document.body.appendChild(script);\n    // @ts-expect-error\n    window.zESettings = props.settings || {};\n    return () => {\n      document.body.removeChild(script);\n    };\n  }, [props]);\n\n  return null;\n}\n","import { DecafClient } from '@decafhub/decaf-client';\nimport React, { ReactNode, useEffect } from 'react';\nimport { DecafAppController, RedirectReason } from './DecafAppController';\nimport DecafVersionChecker from './DecafVersionChecker';\nimport { DecafWebappController } from './DecafWebappController';\nimport { OfflineNotifier } from './OfflineChecker';\nimport ZendeskWidget from './ZendeskWidget';\nimport { DecafContext, Principal, PrivateConfig, PublicConfig } from './context';\n\nexport interface DecafAppConfig {\n  /** version of the application */\n  currentVersion?: string;\n  /** callback when a new version is available */\n  onNewVersion?: (versionOld: string, versionNew: string) => void;\n  /** interval (in seconds) to check for new version */\n  versionCheckInterval?: number;\n  /** interval (in seconds) to check auth status */\n  authCheckInterval?: number;\n  /** Base path of host app.\n   *\n   * This is usually PUBLIC_URL environment variable in React apps.\n   *\n   * Required to enable version checker. */\n  basePath?: string;\n}\nexport interface DecafAppProps {\n  children: ReactNode;\n  config?: DecafAppConfig;\n  controller?: DecafAppController;\n}\n\nexport default function DecafApp(props: DecafAppProps) {\n  const [client, setClient] = React.useState<DecafClient | undefined>(undefined);\n  const [me, setMe] = React.useState<Principal | undefined>(undefined);\n  const [publicConfig, setPublicConfig] = React.useState<PublicConfig | undefined>(undefined);\n  const [privateConfig, setPrivateConfig] = React.useState<PrivateConfig | undefined>(undefined);\n  const [loading, setLoading] = React.useState(true);\n  const [redirectReason, setRedirectReason] = React.useState<RedirectReason>('not-authenticated');\n  const authInterval = React.useRef<NodeJS.Timeout | undefined>(undefined);\n  const controller = props.controller || DecafWebappController;\n\n  function cleanUp(reason: RedirectReason) {\n    setClient(undefined);\n    setMe(undefined);\n    setPublicConfig(undefined);\n    setLoading(false);\n    setRedirectReason(reason);\n  }\n\n  useEffect(() => {\n    controller.onLoadingState(loading);\n  }, [controller, loading]);\n\n  useEffect(() => {\n    const client = controller.getDecafClient();\n\n    if (client) {\n      Promise.all([\n        client.barista.get('/me/'),\n        client.barista.get('/conf/public/'),\n        client.barista.get('/conf/private/'),\n      ])\n        .then(([meResp, configResp, privateConfig]) => {\n          setClient(client);\n          setMe(meResp.data);\n          setPublicConfig(configResp.data);\n          setPrivateConfig(privateConfig.data);\n          setLoading(false);\n        })\n        .catch(() => {\n          cleanUp('session-expired');\n        });\n    } else {\n      cleanUp('not-authenticated');\n    }\n  }, [controller]);\n\n  useEffect(() => {\n    // this is recurring auth check.\n    // Note, we are not building a new client here.\n    // we already know client and me are set.\n    // We just need to check if the credentials are still valid.\n    if (client) {\n      authInterval.current = setInterval(\n        () => {\n          client.barista.get('/me/').catch(({ response }) => {\n            // we are simply ignoring errors other than 401 and 403.\n            if (response.status === 401 || response.status === 403) {\n              cleanUp('session-expired');\n            }\n          });\n        },\n        (props.config?.authCheckInterval || 60) * 1000\n      );\n    }\n    return () => {\n      clearInterval(authInterval.current);\n    };\n  }, [client, props.config?.authCheckInterval]);\n\n  if (loading) {\n    return <>{controller.loadingComponent}</>;\n  } else if (client === undefined || me === undefined || publicConfig === undefined || privateConfig === undefined) {\n    return controller.onInvalidSession(redirectReason);\n  } else {\n    return (\n      <DecafContext.Provider value={{ client, me, publicConfig, privateConfig, controller }}>\n        {props.config?.currentVersion && (\n          <DecafVersionChecker\n            basePath={props.config.basePath}\n            currentVersion={props.config.currentVersion}\n            onNewVersion={props.config.onNewVersion}\n            interval={props.config.versionCheckInterval}\n          />\n        )}\n        {!controller.disableZendeskWidget && publicConfig.zendesk && (\n          <ZendeskWidget\n            zendeskKey={publicConfig.zendesk}\n            settings={{\n              contactForm: {\n                fields: [\n                  {\n                    id: 'name',\n                    prefill: { '*': me.fullname },\n                  },\n                  {\n                    id: 'email',\n                    prefill: { '*': me.email },\n                  },\n                ],\n              },\n            }}\n          />\n        )}\n        {props.children}\n        {!controller?.isNodeApp && <OfflineNotifier />}\n      </DecafContext.Provider>\n    );\n  }\n}\n"],"names":["AbstractDecafNativeController","constructor","client","this","getDecafClient","DecafContext","React","createContext","undefined","me","publicConfig","privateConfig","controller","useDecaf","useContext","DecafVersionChecker","props","newVersion","setNewVersion","useState","interval","useRef","useEffect","current","clearInterval","window","setInterval","_props$basePath","fetch","basePath","Date","getTime","then","res","json","data","version","currentVersion","onNewVersion","catch","console","error","createElement","className","xmlns","width","height","viewBox","fill","d","style","marginRight","onClick","location","reload","DecafSpinner","titleColor","size","color","title","DecafWebappController","token","cookie","document","Error","match","RegExp","decodeURIComponent","getCookie","_authinfo$authenticat","authinfo","JSON","parse","authenticated","_unused","getAuthenticationToken","buildDecafClient","onInvalidSession","reason","href","onLoadingState","_loading","loadingComponent","useConnectionStatus","url","options","_options$interval","_options$slowThreshol","_options$hardTimeout","_options$offlineAfter","_options$slowAfter","slowThreshold","hardTimeout","offlineAfter","slowAfter","status","setStatus","navigator","onLine","slowCount","errorCount","nextTimer","cancelled","runProbe","visibilityState","setTimeout","AbortController","startedAt","now","abortTimer","abort","method","mode","cache","signal","err","DOMException","name","prev","finally","clearTimeout","goOnline","goOffline","addEventListener","removeEventListener","useOnlineStatus","styles","offline_pill","position","bottom","right","display","alignItems","gap","padding","borderRadius","backgroundColor","border","fontSize","fontWeight","lineHeight","zIndex","offline_icon","slow_dot","boxShadow","OfflineNotifier","showSlowIndicator","role","ZendeskWidget","zendeskKey","script","src","ZENDESK_WIDGET_SCRIPT","async","id","body","appendChild","zESettings","settings","removeChild","DecafApp","_props$config2","setClient","setMe","setPublicConfig","setPrivateConfig","loading","setLoading","redirectReason","setRedirectReason","authInterval","cleanUp","Promise","all","barista","get","meResp","configResp","_props$config","response","config","authCheckInterval","Fragment","Provider","value","_props$config3","versionCheckInterval","disableZendeskWidget","zendesk","contactForm","fields","prefill","fullname","email","children","isNodeApp"],"mappings":"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"}