<commit_before>import cuid from "cuid";
import Result, { isError } from "../../common/Result";
import assert from "assert";
import { di, singleton, diKey } from "../../common/di";
import { ILocalFiles, ILocalFilesKey } from "../../common/LocalFiles";
import { IStoreDB, IStoreDBKey, MergeEntity } from "../../common/db/StoreDB";
import {
  ApplicationDto,
  applicationKey,
  CanvasDto,
  DiagramDto,
  DiagramInfoDto,
  DiagramInfoDtos,
  FileDto,
} from "./StoreDtos";
import { LocalEntity } from "../../common/db/LocalDB";
import { RemoteEntity } from "../../common/db/RemoteDB";

export interface Configuration {
  onRemoteChanged: (keys: string[]) => void;
  onSyncChanged: (isOK: boolean) => void;
  isSyncEnabled: boolean;
}

export const IStoreKey = diKey<IStore>();
export interface IStore {
  configure(config: Partial<Configuration>): void;
  triggerSync(): Promise<Result<void>>;

  openNewDiagram(): DiagramDto;
  tryOpenDiagram(diagramId: string): Promise<Result<DiagramDto>>;

  setDiagramName(name: string): void;
  exportDiagram(): DiagramDto; // Used for print or export

  getRootCanvas(): CanvasDto;
  getCanvas(canvasId: string): CanvasDto;
  writeCanvas(canvas: CanvasDto): void;

  getMostResentDiagramId(): Result<string>;
  getRecentDiagrams(): DiagramInfoDto[];

  deleteDiagram(diagramId: string): void;

  saveDiagramToFile(): void;
  loadDiagramFromFile(): Promise<Result<string>>;
  saveAllDiagramsToFile(): Promise<void>;
}

const rootCanvasId = "root";
const defaultApplicationDto: ApplicationDto = { diagramInfos: {} };
const defaultDiagramDto: DiagramDto = { id: "", name: "", canvases: {} };
const resentOpenedMargin = 30 * 1000; // 30 seconds

@singleton(IStoreKey)
export class Store implements IStore {
  private currentDiagramId: string = "";
  private config: Configuration = {
    onRemoteChanged: () => {},
    onSyncChanged: () => {},
    isSyncEnabled: false,
  };

  constructor(
    // private localData: ILocalData = di(ILocalDataKey),
    private localFiles: ILocalFiles = di(ILocalFilesKey),
    private db: IStoreDB = di(IStoreDBKey)
  ) {}

  public configure(config: Partial<Configuration>): void {
    this.config = { ...this.config, ...config };

    this.db.configure({
      onConflict: (local: LocalEntity, remote: RemoteEntity) =>
        this.onEntityConflict(local, remote),
      ...config,
      onRemoteChanged: (keys: string[]) => this.onRemoteChange(keys),
    });
  }

  public triggerSync(): Promise<Result<void>> {
    return this.db.triggerSync();
  }

  public openNewDiagram(): DiagramDto {
    const now = Date.now();
    const id = cuid();
    const name = this.getUniqueName();
    console.log("new diagram", id, name);

    const diagramDto: DiagramDto = {
      id: id,
      name: name,
      canvases: {},
    };

    const applicationDto = this.getApplicationDto();
    applicationDto.diagramInfos[id] = {
      id: id,
      name: name,
      accessed: now,
    };

    this.db.monitorRemoteEntities([id, applicationKey]);
    this.db.writeBatch([
      { key: applicationKey, value: applicationDto },
      { key: id, value: diagramDto },
    ]);

    this.currentDiagramId = id;
    return diagramDto;
  }

  public async tryOpenDiagram(id: string): Promise<Result<DiagramDto>> {
    const diagramDto = await this.db.tryReadLocalThenRemote<DiagramDto>(id);
    if (isError(diagramDto)) {
      return diagramDto;
    }

    this.db.monitorRemoteEntities([id, applicationKey]);
    this.currentDiagramId = id;

    // Tto support most recently used diagram feature, we update accessed time if needed
    const applicationDto = this.getApplicationDto();
    const now = Date.now();
    const diagramInfo = applicationDto.diagramInfos[id];

    console.log(`Access time for ${id} is ${now - diagramInfo.accessed} ms`);
    if (now - diagramInfo.accessed > resentOpenedMargin) {
      // Update last lust update time, since it was a while ago
      console.log(`Updating access time for ${id}`);
      applicationDto.diagramInfos[id] = { ...diagramInfo, accessed: now };
      this.db.writeBatch([{ key: applicationKey, value: applicationDto }]);
    }

    return diagramDto;
  }

  public getRootCanvas(): CanvasDto {
    return this.getCanvas(rootCanvasId);
  }

  public getCanvas(canvasId: string): CanvasDto {
    const diagramDto = this.getDiagramDto();

    const canvasDto = diagramDto.canvases[canvasId];
    assert(canvasDto);

    return canvasDto;
  }

  public writeCanvas(canvasDto: CanvasDto): void {
    const diagramDto = this.getDiagramDto();
    const id = diagramDto.id;

    diagramDto.canvases[canvasDto.id] = canvasDto;

    this.db.writeBatch([{ key: id, value: diagramDto }]);
  }<commit_msg>Fix adjusting last opended<commit_after>import cuid from "cuid";
import Result, { isError } from "../../common/Result";
import assert from "assert";
import { di, singleton, diKey } from "../../common/di";
import { ILocalFiles, ILocalFilesKey } from "../../common/LocalFiles";
import { IStoreDB, IStoreDBKey, MergeEntity } from "../../common/db/StoreDB";
import {
  ApplicationDto,
  applicationKey,
  CanvasDto,
  DiagramDto,
  DiagramInfoDto,
  DiagramInfoDtos,
  FileDto,
} from "./StoreDtos";
import { LocalEntity } from "../../common/db/LocalDB";
import { RemoteEntity } from "../../common/db/RemoteDB";

export interface Configuration {
  onRemoteChanged: (keys: string[]) => void;
  onSyncChanged: (isOK: boolean) => void;
  isSyncEnabled: boolean;
}

export const IStoreKey = diKey<IStore>();
export interface IStore {
  configure(config: Partial<Configuration>): void;
  triggerSync(): Promise<Result<void>>;

  openNewDiagram(): DiagramDto;
  tryOpenDiagram(diagramId: string): Promise<Result<DiagramDto>>;

  setDiagramName(name: string): void;
  exportDiagram(): DiagramDto; // Used for print or export

  getRootCanvas(): CanvasDto;
  getCanvas(canvasId: string): CanvasDto;
  writeCanvas(canvas: CanvasDto): void;

  getMostResentDiagramId(): Result<string>;
  getRecentDiagrams(): DiagramInfoDto[];

  deleteDiagram(diagramId: string): void;

  saveDiagramToFile(): void;
  loadDiagramFromFile(): Promise<Result<string>>;
  saveAllDiagramsToFile(): Promise<void>;
}

const rootCanvasId = "root";
const defaultApplicationDto: ApplicationDto = { diagramInfos: {} };
const defaultDiagramDto: DiagramDto = { id: "", name: "", canvases: {} };
const resentOpenedMargin = 5 * 60 * 1000; // 5 min

@singleton(IStoreKey)
export class Store implements IStore {
  private currentDiagramId: string = "";
  private config: Configuration = {
    onRemoteChanged: () => {},
    onSyncChanged: () => {},
    isSyncEnabled: false,
  };

  constructor(
    // private localData: ILocalData = di(ILocalDataKey),
    private localFiles: ILocalFiles = di(ILocalFilesKey),
    private db: IStoreDB = di(IStoreDBKey)
  ) {}

  public configure(config: Partial<Configuration>): void {
    this.config = { ...this.config, ...config };

    this.db.configure({
      onConflict: (local: LocalEntity, remote: RemoteEntity) =>
        this.onEntityConflict(local, remote),
      ...config,
      onRemoteChanged: (keys: string[]) => this.onRemoteChange(keys),
    });
  }

  public triggerSync(): Promise<Result<void>> {
    return this.db.triggerSync();
  }

  public openNewDiagram(): DiagramDto {
    const now = Date.now();
    const id = cuid();
    const name = this.getUniqueName();
    console.log("new diagram", id, name);

    const diagramDto: DiagramDto = {
      id: id,
      name: name,
      canvases: {},
    };

    const applicationDto = this.getApplicationDto();
    applicationDto.diagramInfos[id] = {
      id: id,
      name: name,
      accessed: now,
    };

    this.db.monitorRemoteEntities([id, applicationKey]);
    this.db.writeBatch([
      { key: applicationKey, value: applicationDto },
      { key: id, value: diagramDto },
    ]);

    this.currentDiagramId = id;
    return diagramDto;
  }

  public async tryOpenDiagram(id: string): Promise<Result<DiagramDto>> {
    const diagramDto = await this.db.tryReadLocalThenRemote<DiagramDto>(id);
    if (isError(diagramDto)) {
      return diagramDto;
    }

    this.db.monitorRemoteEntities([id, applicationKey]);
    const isSameDiagram = this.currentDiagramId === id;
    this.currentDiagramId = id;

    // Tto support most recently used diagram feature, we update accessed time if needed
    const applicationDto = this.getApplicationDto();
    const now = Date.now();
    const diagramInfo = applicationDto.diagramInfos[id];

    console.log(`Access time for ${id} is ${now - diagramInfo.accessed} ms`);
    if (!isSameDiagram || now - diagramInfo.accessed > resentOpenedMargin) {
      // Update last lust update time, since it a new diagram or was a while ago
      console.log(`Updating access time for ${id}`);
      applicationDto.diagramInfos[id] = { ...diagramInfo, accessed: now };
      this.db.writeBatch([{ key: applicationKey, value: applicationDto }]);
    }

    return diagramDto;
  }

  public getRootCanvas(): CanvasDto {
    return this.getCanvas(rootCanvasId);
  }

  public getCanvas(canvasId: string): CanvasDto {
    const diagramDto = this.getDiagramDto();

    const canvasDto = diagramDto.canvases[canvasId];
    assert(canvasDto);

    return canvasDto;
  }

  public writeCanvas(canvasDto: CanvasDto): void {
    const diagramDto = this.getDiagramDto();
    const id = diagramDto.id;

    diagramDto.canvases[canvasDto.id] = canvasDto;

    this.db.writeBatch([{ key: id, value: diagramDto }]);
  }
{'fraction_non_alphanumeric': 0.13543970510795156, 'fraction_numerical': 0.0016850974196945762, 'mean_word_length': 3.7983830217281453, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7231416', 'n_tokens_mistral': 2928, 'n_tokens_neox': 2985, 'n_words': 701}