about summary refs log tree commit diff
path: root/src/components/PolicyUpdateOverlay/usePolicyUpdateState.ts
blob: 32b948af9d30073eab594dc51f9791a0dd5258c1 (plain) (blame)
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import {useMemo} from 'react'

import {useNux, useSaveNux} from '#/state/queries/nuxs'
import {ACTIVE_UPDATE_ID} from '#/components/PolicyUpdateOverlay/config'
import {logger} from '#/components/PolicyUpdateOverlay/logger'
import {IS_DEV} from '#/env'
import {device, useStorage} from '#/storage'

export type PolicyUpdateState = {
  completed: boolean
  complete: () => void
}

export function usePolicyUpdateState({
  enabled,
}: {
  /**
   * Used to skip the policy update overlay until we're actually ready to
   * show it.
   */
  enabled: boolean
}) {
  const nux = useNux(ACTIVE_UPDATE_ID)
  const {mutate: save, variables} = useSaveNux()
  const deviceStorage = useStorage(device, [ACTIVE_UPDATE_ID])
  const debugOverride =
    !!useStorage(device, ['policyUpdateDebugOverride'])[0] && IS_DEV

  return useMemo(() => {
    /**
     * If not enabled, then just return a completed state so the app functions
     * as normal.
     */
    if (!enabled) {
      return {
        completed: true,
        complete() {},
      }
    }

    const nuxIsReady = nux.status === 'ready'
    const nuxIsCompleted = nux.nux?.completed === true
    const nuxIsOptimisticallyCompleted = !!variables?.completed
    const [completedForDevice, setCompletedForDevice] = deviceStorage

    const completed = computeCompletedState({
      nuxIsReady,
      nuxIsCompleted,
      nuxIsOptimisticallyCompleted,
      completedForDevice,
    })

    logger.debug(`state`, {
      completed,
      nux,
      completedForDevice,
    })

    if (!debugOverride) {
      syncCompletedState({
        nuxIsReady,
        nuxIsCompleted,
        nuxIsOptimisticallyCompleted,
        completedForDevice,
        save,
        setCompletedForDevice,
      })
    }

    return {
      completed,
      complete() {
        logger.debug(`user completed`)
        save({
          id: ACTIVE_UPDATE_ID,
          completed: true,
          data: undefined,
        })
        setCompletedForDevice(true)
      },
    }
  }, [enabled, nux, save, variables, deviceStorage, debugOverride])
}

export function computeCompletedState({
  nuxIsReady,
  nuxIsCompleted,
  nuxIsOptimisticallyCompleted,
  completedForDevice,
}: {
  nuxIsReady: boolean
  nuxIsCompleted: boolean
  nuxIsOptimisticallyCompleted: boolean
  completedForDevice: boolean | undefined
}): boolean {
  /**
   * Assume completed to prevent flash
   */
  let completed = true

  /**
   * Prefer server state, if available
   */
  if (nuxIsReady) {
    completed = nuxIsCompleted
  }

  /**
   * Override with optimistic state or device state
   */
  if (nuxIsOptimisticallyCompleted || !!completedForDevice) {
    completed = true
  }

  return completed
}

export function syncCompletedState({
  nuxIsReady,
  nuxIsCompleted,
  nuxIsOptimisticallyCompleted,
  completedForDevice,
  save,
  setCompletedForDevice,
}: {
  nuxIsReady: boolean
  nuxIsCompleted: boolean
  nuxIsOptimisticallyCompleted: boolean
  completedForDevice: boolean | undefined
  save: ReturnType<typeof useSaveNux>['mutate']
  setCompletedForDevice: (value: boolean) => void
}) {
  /*
   * Sync device state to server state for this account
   */
  if (
    nuxIsReady &&
    !nuxIsCompleted &&
    !nuxIsOptimisticallyCompleted &&
    !!completedForDevice
  ) {
    logger.debug(`syncing device state to server state`)
    save({
      id: ACTIVE_UPDATE_ID,
      completed: true,
      data: undefined,
    })
  } else if (nuxIsReady && nuxIsCompleted && !completedForDevice) {
    logger.debug(`syncing server state to device state`)
    /*
     * Sync server state to device state
     */
    setCompletedForDevice(true)
  }
}