Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ea518ec4 authored by Raj Khemani's avatar Raj Khemani
Browse files

initial support for local zip files

parent 269c0263
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -380,6 +380,25 @@
        <div class="card-header" data-translate="download-eos"></div>
        <div class="card-body">
          <p data-translate="this-might-take-some-time-please-be-patient"></p>
          <div class="download-actions text-center">
            <div class="download-buttons">
              <button class="next download-build-button">
                Download build
                <span class="btn-loader"></span>
              </button>
              <button class="next use-local-zip-button">
                Use local ZIP
                <span class="btn-loader"></span>
              </button>
            </div>
            <input
              class="local-zip-input"
              type="file"
              accept=".zip"
              hidden
            />
            <p class="local-zip-error" style="display: none; color: red"></p>
          </div>
          <div class="text-center large-padding">
            <p class="downloading-progress"></p>
            <progress class="downloading-progress-bar" max="100"></progress>
+9 −1
Original line number Diff line number Diff line
@@ -413,7 +413,7 @@ export class Controller {
  setResources(resources) {
    this.resources = resources;
    if (this.resources.steps) {
      this.steps.push(new Step("downloading", "download", false));
      this.steps.push(new Step("downloading", "download", true));
      this.steps.push(
        ...this.resources.steps.map((step) => {
          return new Step(
@@ -428,4 +428,12 @@ export class Controller {
    }
    this.deviceManager.setResources(this.resources.folder, this.steps);
  }

  setLocalZip(file) {
    this.deviceManager.setLocalZipFile(file);
  }

  clearLocalZip() {
    this.deviceManager.clearLocalZipFile();
  }
}
+29 −7
Original line number Diff line number Diff line
@@ -27,6 +27,18 @@ export class DeviceManager {
    this.wasConnected = false;
  }

  setLocalZipFile(file) {
    this.downloader.setLocalZip(file);
  }

  clearLocalZipFile() {
    this.downloader.clearLocalZip();
  }

  hasLocalZipFile() {
    return this.downloader.hasLocalZip();
  }

  async init() {
    await this.bootloader.init();
    await this.adb.init();
@@ -187,6 +199,15 @@ export class DeviceManager {

  async downloadAll(onProgress, onUnzip, onVerify) {
    try {
      if (this.downloader.hasLocalZip()) {
        await this.downloader.ingestLocalZip(
          this.files,
          this.folder,
          onProgress,
          onUnzip,
          onVerify,
        );
      } else {
        await this.downloader.downloadAndUnzipFolder(
          this.files,
          this.folder,
@@ -194,6 +215,7 @@ export class DeviceManager {
          onUnzip,
          onVerify,
        );
      }
    } catch (e) {
      throw new Error(`downloadAll error ${e.message || e}`);
    }
+63 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ export class Downloader {
  constructor() {
    this.db = null;
    this.stored = {};
    this.localZipFile = null;
  }

  async init() {
@@ -25,6 +26,18 @@ export class Downloader {
    this.quota = await navigator.storage.estimate();
  }

  setLocalZip(file) {
    this.localZipFile = file;
  }

  clearLocalZip() {
    this.localZipFile = null;
  }

  hasLocalZip() {
    return !!this.localZipFile;
  }

  /*
   * */
  async downloadAndUnzipFolder(
@@ -34,6 +47,8 @@ export class Downloader {
    onUnzipProgress,
    onVerifyProgress,
  ) {
    await this.clearDBStore();
    this.stored = {};
    let current_file;
    try {
      for (let i = 0; i < folder.length; i++) {
@@ -117,6 +132,54 @@ export class Downloader {
    }
  }

  async ingestLocalZip(
    filesRequired,
    folder,
    onDownloadProgress,
    onUnzipProgress,
    onVerifyProgress,
  ) {
    await this.clearDBStore();
    this.stored = {};
    if (!this.localZipFile) {
      throw new Error("No local zip file selected");
    }
    const zipDescriptor = folder.find((f) => f.unzip) || folder[0];
    if (!zipDescriptor) {
      throw new Error("No zip resource found to map local file");
    }

    onDownloadProgress(
      this.localZipFile.size,
      this.localZipFile.size,
      this.localZipFile.name || zipDescriptor.name || "local.zip",
    );

    const zipReader = new ZipReader(new BlobReader(this.localZipFile));
    const filesEntries = await zipReader.getEntries();
    for (let i = 0; i < filesEntries.length; i++) {
      const unzippedEntry = await this.getFileFromZip(filesEntries[i], (value, total) => {
        onUnzipProgress(value, total, filesEntries[i].filename);
      });
      let filename = this.getMappedName(
        filesEntries[i].filename,
        zipDescriptor.mapping,
      );
      if (filesRequired.includes(filename)) {
        await this.setInDBStore(unzippedEntry.blob, filename);
        this.stored[filename] = true;
        const fileSHA = await this.computeSha256(
          unzippedEntry.blob,
          (loaded, total) => {
            onVerifyProgress(loaded, total, filename);
          },
        );
        console.log(`File: ${unzippedEntry.name} SHA256: ${fileSHA}`);
      }
    }
    await zipReader.close();
  }

  async getFileFromZip(file, onProgress) {
    const name = file.filename;
    const blob = await file.getData(new BlobWriter(), {
+64 −8
Original line number Diff line number Diff line
@@ -32,6 +32,9 @@ export default class ViewManager {
      $copyStep.id = step.id;
      $copyStep.classList.add("active");
      $copyStep.classList.remove("inactive");
      if (step.name === "downloading") {
        this.bindDownloadChoice($copyStep, step);
      } else {
        const $button = $copyStep.querySelector("button");
        if ($button) {
          $button.addEventListener("click", async (event) => {
@@ -39,6 +42,7 @@ export default class ViewManager {
            await this.executeStep($button, step.name);
          });
        }
      }
      let $processCtn = document.getElementById("process-ctn");
      if ($processCtn) {
        $processCtn.appendChild($copyStep);
@@ -222,6 +226,57 @@ export default class ViewManager {
  }

  // /CONTROLLER EVENTS
  bindDownloadChoice($copyStep, step) {
    const downloadBtn = $copyStep.querySelector(".download-build-button");
    const localBtn = $copyStep.querySelector(".use-local-zip-button");
    const fileInput = $copyStep.querySelector(".local-zip-input");
    const errorEl = $copyStep.querySelector(".local-zip-error");

    if (downloadBtn) {
      downloadBtn.addEventListener("click", async (event) => {
        event.stopPropagation();
        if (errorEl) {
          errorEl.style.display = "none";
          errorEl.innerText = "";
        }
        this.controller.clearLocalZip();
        await this.executeStep(downloadBtn, step.name);
      });
    }

    if (localBtn && fileInput) {
      localBtn.addEventListener("click", (event) => {
        event.stopPropagation();
        if (errorEl) {
          errorEl.style.display = "none";
          errorEl.innerText = "";
        }
        fileInput.value = "";
        fileInput.click();
      });

      fileInput.addEventListener("change", async (evt) => {
        const file = evt.target.files[0];
        if (!file) {
          return;
        }
        if (!file.name.toLowerCase().endsWith(".zip")) {
          if (errorEl) {
            errorEl.innerText = "Please select a .zip file.";
            errorEl.style.display = "block";
          }
          fileInput.value = "";
          return;
        }
        if (errorEl) {
          errorEl.style.display = "none";
          errorEl.innerText = "";
        }
        this.controller.setLocalZip(file);
        await this.executeStep(localBtn, step.name);
      });
    }
  }
}

document.addEventListener("DOMContentLoaded", async () => {
@@ -230,11 +285,12 @@ document.addEventListener("DOMContentLoaded", async () => {

  let elts = document.querySelectorAll(".card button");
  for (let elt of elts) {
    if (elt.parentElement.parentElement.className.includes("inactive")) {
    const card = elt.closest(".card");
    if (!card || card.className.includes("inactive")) {
      continue;
    }
    elt.addEventListener("click", async () => {
      VIEW.executeStep(elt, elt.parentElement.parentElement.id);
      VIEW.executeStep(elt, card.id);
    });
  }
});