ファイル監視ツール開発日誌20210131

ファイル監視ツールの方をちょくちょく進めてまして、正規表現での検索は動くようになりました。 f:id:steavevaivai:20210131233635g:plain 正規表現の条件を複数設定できるので、いくつかの条件でまとめて絞ってみたい場合などはこれで結構楽に管理できるかもしれないです。最初は検索だけできればよい気がしていたのですがせっかくブラウザで動かすのでfilterかけたりmapしたり出来ても良いのかもしれないと作っている最中に思いました。

その他気を付けた点についてですが、ファイルの読み込みはScalaで行っていてWebsocketでJavascript側に送っているのですが大きいファイルをJavascript側にためこむのは不便で、それからScala側はstreamで開いているので都度都度ほしい部分を読み込むのは面倒だったのでJavascript側でIndexedDBを使って永続化していました。IndexedDBはKey指定でObjectを保存できるので文字列しか保存できないLocalStorageに比べて大分使いやすいもので、以下に説明があります。

developer.mozilla.org

ファイルのキャッシュでは具体的には以下のDatabase単位のDbService クラスとStore単位のFileCacheRepo クラスを使ってIndexedDBに保存していました。

class DbService {
  constructor() {
    this.dbName = "myDb";
    this.dbVersion = 1;
    this.callbackActions = [];
    this.stores = [];
  }

  addStore(store) {
    this.stores.push(store);
  }

  init() {
    const request = indexedDB.open(this.dbName, this.dbVersion);
    request.onerror = (event) => {
      console.log("Error init db");
      indexedDB.deleteDatabase(this.dbName);
    };
    request.onupgradeneeded = (event) => {
      this.dbCon = event.target.result;
      Array.from(this.dbCon.objectStoreNames).forEach((storeName) =>
        this.dbCon.deleteObjectStore(storeName)
      );
      this.stores.forEach((store) => {
        this.dbCon.createObjectStore(store[0], store[1]);
      });
    };
    request.onsuccess = (event) => {
      this.dbCon = event.target.result;
      while (true) {
        if (this.callbackActions.length == 0) {
          break;
        } else {
          this.callbackActions.shift()();
        }
      }
    };
  }
  save(storName, datas) {
    const transaction = this.dbCon.transaction([storName], "readwrite");
    transaction.onerror = (e) => console.log("indexed db command error");
    var store = transaction.objectStore(storName);
    datas.forEach((d) => store.put(d));
  }
  delete(storName, datas) {
    const transaction = this.dbCon.transaction([storName], "readwrite");
    transaction.onerror = (e) => console.log("indexed db command error");
    var store = transaction.objectStore(storName);
    datas.forEach((d) => store.delete(d));
  }

  query(storName, key, action) {
    const transaction = this.dbCon.transaction([storName], "readonly");
    transaction.onerror = (e) => console.log("indexed db querry error");
    var store = transaction.objectStore(storName);
    store.get(key).onsuccess = function (event) {
      const data = event.target.result;
      action(data);
    };
  }

  getAll(storName, action) {
    const f = ((action) => () => {
      const transaction = this.dbCon.transaction([storName], "readonly");
      var objectStore = transaction.objectStore(storName);
      objectStore.getAll().onsuccess = function (event) {
        const rows = event.target.result;
        action(rows);
      };
    })(action);
    if (this.dbCon) {
      f();
    } else {
      this.callbackActions.push(f);
    }
  }

  clear(storeName) {
    const transaction = this.dbCon.transaction([storeName], "readwrite");
    transaction.onerror = (e) => console.log("indexed db command error");
    var store = transaction.objectStore(storeName);
    store.clear();
  }
}

class FileCacheRepo {
  constructor(dbService) {
    this.dbService = dbService;
    this.storeName = "fileCache";
    this.dbService.addStore([this.storeName, { keyPath: "index" }]);
  }

  get(index, action) {
    this.dbService.query(this.storeName, index, action);
  }

  save(data) {
    this.dbService.save(this.storeName, [data]);
  }
  delete(datas) {
    this.dbService.delete(this.storeName, datas);
  }
  clear() {
    this.dbService.clear(this.storeName);
  }
}

const dbService = new DbService();
const fileCacheRepo = new FileCacheRepo(dbService);
dbService.init();

これで以下のように保存ができています。 f:id:steavevaivai:20210131235417p:plain

またスクロールの見せ方について、大きなファイルを開くときにチャットツールなどではスクロールで読み込んだタイミングでDomを追加したりするのが多いと思いますがDomの追加、削除のタイミングでスクロールバーが 移動したりしてスクロールし続ける操作がしずらかったので以下のようにボタンを押してスクロールという風にしてみました。ボタンを押した回数により移動速度も調整できるようにしています。 f:id:steavevaivai:20210131235742g:plain