1import * as Updates from './Updates';
2import type {
3  Manifest,
4  UpdatesNativeStateMachineContext,
5  UpdatesNativeStateRollback,
6} from './Updates.types';
7import { UpdateInfoType, type CurrentlyRunningInfo, type UpdateInfo } from './UseUpdates.types';
8
9// The currently running info, constructed from Updates constants
10export const currentlyRunning: CurrentlyRunningInfo = {
11  updateId: Updates.updateId ?? undefined,
12  channel: Updates.channel ?? undefined,
13  createdAt: Updates.createdAt ?? undefined,
14  isEmbeddedLaunch: Updates.isEmbeddedLaunch,
15  isEmergencyLaunch: Updates.isEmergencyLaunch,
16  manifest: Updates.manifest ?? undefined,
17  runtimeVersion: Updates.runtimeVersion ?? undefined,
18};
19
20// Type for the state managed by useUpdates().
21// Used internally by this module and not exported publicly.
22export type UseUpdatesStateType = {
23  availableUpdate?: UpdateInfo;
24  downloadedUpdate?: UpdateInfo;
25  checkError?: Error;
26  downloadError?: Error;
27  initializationError?: Error;
28  isUpdateAvailable: boolean;
29  isUpdatePending: boolean;
30  isChecking: boolean;
31  isDownloading: boolean;
32  lastCheckForUpdateTimeSinceRestart?: Date;
33};
34
35// Constructs an UpdateInfo from a manifest
36export const updateFromManifest: (manifest: NonNullable<Manifest>) => UpdateInfo = (manifest) => {
37  return {
38    type: UpdateInfoType.NEW,
39    updateId: manifest.id ?? '',
40    createdAt:
41      manifest && 'createdAt' in manifest && manifest.createdAt
42        ? new Date(manifest.createdAt)
43        : // We should never reach this if the manifest is valid and has a commit time,
44          // but leave this in so that createdAt is always defined
45          new Date(0),
46    manifest,
47  };
48};
49
50export const updateFromRollback: (rollback: UpdatesNativeStateRollback) => UpdateInfo = (
51  rollback
52) => ({
53  type: UpdateInfoType.ROLLBACK,
54  createdAt: new Date(rollback.commitTime),
55  manifest: undefined,
56  updateId: undefined,
57});
58
59// Default useUpdates() state
60export const defaultUseUpdatesState: UseUpdatesStateType = {
61  isChecking: false,
62  isDownloading: false,
63  isUpdateAvailable: false,
64  isUpdatePending: false,
65};
66
67// Transform the useUpdates() state based on native state machine context
68export const reduceUpdatesStateFromContext: (
69  updatesState: UseUpdatesStateType,
70  context: UpdatesNativeStateMachineContext
71) => UseUpdatesStateType = (updatesState, context) => {
72  const availableUpdate = context?.latestManifest
73    ? updateFromManifest(context?.latestManifest)
74    : context.rollback
75    ? updateFromRollback(context.rollback)
76    : undefined;
77  const downloadedUpdate = context?.downloadedManifest
78    ? updateFromManifest(context?.downloadedManifest)
79    : context.rollback
80    ? updateFromRollback(context.rollback)
81    : undefined;
82  return {
83    ...updatesState,
84    isUpdateAvailable: context.isUpdateAvailable,
85    isUpdatePending: context.isUpdatePending,
86    isChecking: context.isChecking,
87    isDownloading: context.isDownloading,
88    availableUpdate,
89    downloadedUpdate,
90    checkError: context.checkError,
91    downloadError: context.downloadError,
92    lastCheckForUpdateTimeSinceRestart: context.lastCheckForUpdateTime,
93  };
94};
95