about summary refs log tree commit diff
path: root/src/components/PolicyUpdateOverlay/usePolicyUpdateState.ts
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2025-08-06 15:15:52 -0500
committerGitHub <noreply@github.com>2025-08-06 15:15:52 -0500
commit328aa2be9482f77cb1cf86c5d227fdcee9981b16 (patch)
tree27174f10e0fe80288c0cd6907f8686486131d082 /src/components/PolicyUpdateOverlay/usePolicyUpdateState.ts
parentfd37d92f85ddf0f075a67c4e9b2d85bef38f1835 (diff)
downloadvoidsky-328aa2be9482f77cb1cf86c5d227fdcee9981b16.tar.zst
[APP-1356] Policy update dialog (#8782)
* Add blocking announcement dialog feature

* WIP custom dialog

* Rework dialog and add native FocusScope

* Lock scroll on web, fix backdrop

* Add web FocusScope

* Create custom Outlet for these announcements

* Clean up FocusScope native impl

* Comments

* Some styling fixes

* Handle screen reader specifically

* Clean up state, remove Portal edits

* Reorg, rename

* Add syncing, tests

* Revert dialog updates

* Revert formatting

* Delete unused file

* Format

* Add FullWindowOverlay

* remove mmkv storage in debug btn

* Add debug code

* fix taps passing through on iOS

* Reorg

* Reorg, rename everything

* Complete policy update after signup

* Add logger

* Move context around, unmount portals on native

* Move a11y prop into FocusScope

* Remove useMemo

* Update dates

* Move debug to dev settings

* Unmount web portals until policy update completed

* UPdate dates

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>
Diffstat (limited to 'src/components/PolicyUpdateOverlay/usePolicyUpdateState.ts')
-rw-r--r--src/components/PolicyUpdateOverlay/usePolicyUpdateState.ts135
1 files changed, 135 insertions, 0 deletions
diff --git a/src/components/PolicyUpdateOverlay/usePolicyUpdateState.ts b/src/components/PolicyUpdateOverlay/usePolicyUpdateState.ts
new file mode 100644
index 000000000..29d8afe06
--- /dev/null
+++ b/src/components/PolicyUpdateOverlay/usePolicyUpdateState.ts
@@ -0,0 +1,135 @@
+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() {
+  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(() => {
+    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)
+      },
+    }
+  }, [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)
+  }
+}