<commit_before>import * as remote from '@electron/remote'
import { ipcRenderer } from 'electron'
const lastSuccessfulCheckKey = 'last-successful-update-check'

import { Emitter, Disposable } from 'event-kit'

import { sendWillQuitSync } from '../main-process-proxy'
import { ErrorWithMetadata } from '../../lib/error-with-metadata'
import { parseError } from '../../lib/squirrel-error-parser'

import { ReleaseSummary } from '../../models/release-notes'
import { generateReleaseSummary } from '../../lib/release-notes'
import { setNumber, getNumber } from '../../lib/local-storage'
import { enableUpdateFromEmulatedX64ToARM64 } from '../../lib/feature-flag'
import { isRunningUnderARM64Translation } from 'detect-arm64-translation'

/** The states the auto updater can be in. */
export enum UpdateStatus {
  /** The auto updater is checking for updates. */
  CheckingForUpdates,

  /** An update is available and will begin downloading. */
  UpdateAvailable,

  /** No update is available. */
  UpdateNotAvailable,

  /** An update has been downloaded and is ready to be installed. */
  UpdateReady,
}

export interface IUpdateState {
  status: UpdateStatus
  lastSuccessfulCheck: Date | null
  newRelease: ReleaseSummary | null
}

/** A store which contains the current state of the auto updater. */
class UpdateStore {
  private emitter = new Emitter()
  private status = UpdateStatus.UpdateNotAvailable
  private lastSuccessfulCheck: Date | null = null
  private newRelease: ReleaseSummary | null = null

  /** Is the most recent update check user initiated? */
  private userInitiatedUpdate = true

  public constructor() {
    const lastSuccessfulCheckTime = getNumber(lastSuccessfulCheckKey, 0)

    if (lastSuccessfulCheckTime > 0) {
      this.lastSuccessfulCheck = new Date(lastSuccessfulCheckTime)
    }

    try {
      ipcRenderer.on('auto-updater-error', (evt, error) => {
        this.onAutoUpdaterError(error)
      })

      ipcRenderer.on('auto-updater-checking-for-update', (evt, error) => {
        this.onCheckingForUpdate()
      })

      ipcRenderer.on('auto-updater-update-available', (evt, error) => {
        this.onUpdateAvailable()
      })

      ipcRenderer.on('auto-updater-update-not-available', (evt, error) => {
        this.onUpdateNotAvailable()
      })

      ipcRenderer.on('auto-updater-update-downloaded', (evt, error) => {
        this.onUpdateDownloaded()
      })

      ipcRenderer.invoke('setup-auto-updater')

      window.addEventListener('beforeunload', () => {
        ipcRenderer.invoke('dispose-auto-updater')
      })
    } catch (e) {
      this.emitError(e)
    }
  }

  private touchLastChecked() {
    const now = new Date()
    this.lastSuccessfulCheck = now
    setNumber(lastSuccessfulCheckKey, now.getTime())
  }

  private onAutoUpdaterError = (error: Error) => {
    this.status = UpdateStatus.UpdateNotAvailable

    if (__WIN32__) {
      const parsedError = parseError(error)
      this.emitError(parsedError || error)
    } else {
      this.emitError(error)
    }
  }

  private onCheckingForUpdate = () => {
    this.status = UpdateStatus.CheckingForUpdates
    this.emitDidChange()
  }

  private onUpdateAvailable = () => {
    this.touchLastChecked()
    this.status = UpdateStatus.UpdateAvailable
    this.emitDidChange()
  }

  private onUpdateNotAvailable = () => {
    this.touchLastChecked()
    this.status = UpdateStatus.UpdateNotAvailable
    this.emitDidChange()
  }

  private onUpdateDownloaded = async () => {
    this.newRelease = await generateReleaseSummary()

    this.status = UpdateStatus.UpdateReady

    this.emitDidChange()
  }

  /** Register a function to call when the auto updater state changes. */
  public onDidChange(fn: (state: IUpdateState) => void): Disposable {
    return this.emitter.on('did-change', fn)
  }

  private emitDidChange() {
    this.emitter.emit('did-change', this.state)
  }

  /** Register a function to call when the auto updater encounters an error. */
  public onError(fn: (error: Error) => void): Disposable {
    return this.emitter.on('error', fn)
  }

  private emitError(error: Error) {
    const updatedError = new ErrorWithMetadata(error, {
      backgroundTask: !this.userInitiatedUpdate,
    })
    this.emitter.emit('error', updatedError)
  }

  /** The current auto updater state. */
  public get state(): IUpdateState {
    return {
      status: this.status,
      lastSuccessfulCheck: this.lastSuccessfulCheck,
      newRelease: this.newRelease,
    }
  }

  /**
   * Check for updates.
   *
   * @param inBackground - Are we checking for updates in the background, or was
   *                       this check user-initiated?
   */
  public async checkForUpdates(inBackground: boolean) {
    // An update has been downloaded and the app is waiting to be restarted.
    // Checking for updates again may result in the running app being nuked
    // when it finds a subsequent update.
    if (__WIN32__ && this.status === UpdateStatus.UpdateReady) {
      return
    }

    let updatesURL = __UPDATES_URL__

    // If the app is running under Rosetta (i.e. it's a macOS x64 binary running
    // on an arm64 machine), we need to tweak the update URL here to point at
    // the arm64 binary.
    if (
      enableUpdateFromEmulatedX64ToARM64() &&
      (remote.app.runningUnderRosettaTranslation === true ||
        isRunningUnderARM64Translation() === true)
    ) {
      const url = new URL(updatesURL)
      url.pathname = url.pathname.replace(
        /\/desktop\/desktop\/(x64\/)?latest/,
        '/desktop/desktop/arm64/latest'
      )
      updatesURL = url.toString()
    }

    this.userInitiatedUpdate = !inBackground

    const checkedResults = await ipcRenderer.invoke(
      'check-for-updates',
      updatesURL
    )

    if (checkedResults !== null && checkedResults !== undefined) {
      this.emitError(checkedResults)
    }<commit_msg>Better variable name<commit_after>import * as remote from '@electron/remote'
import { ipcRenderer } from 'electron'
const lastSuccessfulCheckKey = 'last-successful-update-check'

import { Emitter, Disposable } from 'event-kit'

import { sendWillQuitSync } from '../main-process-proxy'
import { ErrorWithMetadata } from '../../lib/error-with-metadata'
import { parseError } from '../../lib/squirrel-error-parser'

import { ReleaseSummary } from '../../models/release-notes'
import { generateReleaseSummary } from '../../lib/release-notes'
import { setNumber, getNumber } from '../../lib/local-storage'
import { enableUpdateFromEmulatedX64ToARM64 } from '../../lib/feature-flag'
import { isRunningUnderARM64Translation } from 'detect-arm64-translation'

/** The states the auto updater can be in. */
export enum UpdateStatus {
  /** The auto updater is checking for updates. */
  CheckingForUpdates,

  /** An update is available and will begin downloading. */
  UpdateAvailable,

  /** No update is available. */
  UpdateNotAvailable,

  /** An update has been downloaded and is ready to be installed. */
  UpdateReady,
}

export interface IUpdateState {
  status: UpdateStatus
  lastSuccessfulCheck: Date | null
  newRelease: ReleaseSummary | null
}

/** A store which contains the current state of the auto updater. */
class UpdateStore {
  private emitter = new Emitter()
  private status = UpdateStatus.UpdateNotAvailable
  private lastSuccessfulCheck: Date | null = null
  private newRelease: ReleaseSummary | null = null

  /** Is the most recent update check user initiated? */
  private userInitiatedUpdate = true

  public constructor() {
    const lastSuccessfulCheckTime = getNumber(lastSuccessfulCheckKey, 0)

    if (lastSuccessfulCheckTime > 0) {
      this.lastSuccessfulCheck = new Date(lastSuccessfulCheckTime)
    }

    try {
      ipcRenderer.on('auto-updater-error', (evt, error) => {
        this.onAutoUpdaterError(error)
      })

      ipcRenderer.on('auto-updater-checking-for-update', (evt, error) => {
        this.onCheckingForUpdate()
      })

      ipcRenderer.on('auto-updater-update-available', (evt, error) => {
        this.onUpdateAvailable()
      })

      ipcRenderer.on('auto-updater-update-not-available', (evt, error) => {
        this.onUpdateNotAvailable()
      })

      ipcRenderer.on('auto-updater-update-downloaded', (evt, error) => {
        this.onUpdateDownloaded()
      })

      ipcRenderer.invoke('setup-auto-updater')

      window.addEventListener('beforeunload', () => {
        ipcRenderer.invoke('dispose-auto-updater')
      })
    } catch (e) {
      this.emitError(e)
    }
  }

  private touchLastChecked() {
    const now = new Date()
    this.lastSuccessfulCheck = now
    setNumber(lastSuccessfulCheckKey, now.getTime())
  }

  private onAutoUpdaterError = (error: Error) => {
    this.status = UpdateStatus.UpdateNotAvailable

    if (__WIN32__) {
      const parsedError = parseError(error)
      this.emitError(parsedError || error)
    } else {
      this.emitError(error)
    }
  }

  private onCheckingForUpdate = () => {
    this.status = UpdateStatus.CheckingForUpdates
    this.emitDidChange()
  }

  private onUpdateAvailable = () => {
    this.touchLastChecked()
    this.status = UpdateStatus.UpdateAvailable
    this.emitDidChange()
  }

  private onUpdateNotAvailable = () => {
    this.touchLastChecked()
    this.status = UpdateStatus.UpdateNotAvailable
    this.emitDidChange()
  }

  private onUpdateDownloaded = async () => {
    this.newRelease = await generateReleaseSummary()

    this.status = UpdateStatus.UpdateReady

    this.emitDidChange()
  }

  /** Register a function to call when the auto updater state changes. */
  public onDidChange(fn: (state: IUpdateState) => void): Disposable {
    return this.emitter.on('did-change', fn)
  }

  private emitDidChange() {
    this.emitter.emit('did-change', this.state)
  }

  /** Register a function to call when the auto updater encounters an error. */
  public onError(fn: (error: Error) => void): Disposable {
    return this.emitter.on('error', fn)
  }

  private emitError(error: Error) {
    const updatedError = new ErrorWithMetadata(error, {
      backgroundTask: !this.userInitiatedUpdate,
    })
    this.emitter.emit('error', updatedError)
  }

  /** The current auto updater state. */
  public get state(): IUpdateState {
    return {
      status: this.status,
      lastSuccessfulCheck: this.lastSuccessfulCheck,
      newRelease: this.newRelease,
    }
  }

  /**
   * Check for updates.
   *
   * @param inBackground - Are we checking for updates in the background, or was
   *                       this check user-initiated?
   */
  public async checkForUpdates(inBackground: boolean) {
    // An update has been downloaded and the app is waiting to be restarted.
    // Checking for updates again may result in the running app being nuked
    // when it finds a subsequent update.
    if (__WIN32__ && this.status === UpdateStatus.UpdateReady) {
      return
    }

    let updatesURL = __UPDATES_URL__

    // If the app is running under Rosetta (i.e. it's a macOS x64 binary running
    // on an arm64 machine), we need to tweak the update URL here to point at
    // the arm64 binary.
    if (
      enableUpdateFromEmulatedX64ToARM64() &&
      (remote.app.runningUnderRosettaTranslation === true ||
        isRunningUnderARM64Translation() === true)
    ) {
      const url = new URL(updatesURL)
      url.pathname = url.pathname.replace(
        /\/desktop\/desktop\/(x64\/)?latest/,
        '/desktop/desktop/arm64/latest'
      )
      updatesURL = url.toString()
    }

    this.userInitiatedUpdate = !inBackground

    const error = await ipcRenderer.invoke('check-for-updates', updatesURL)

    if (error !== null && error !== undefined) {
      this.emitError(error)
    }
{'fraction_non_alphanumeric': 0.11502087415864361, 'fraction_numerical': 0.005112038851495271, 'mean_word_length': 3.060186786579038, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6249581', 'n_tokens_mistral': 3491, 'n_tokens_neox': 3387, 'n_words': 865}