2026-04-18 11:55:01 +00:00
|
|
|
import { v4 } from "uuid";
|
|
|
|
|
import BoardsAndTasksModel from "./boardsAndTasksModel";
|
2024-07-22 11:49:05 +02:00
|
|
|
import {
|
2026-04-01 08:29:39 +00:00
|
|
|
StringEntryObject,
|
|
|
|
|
createTimestamp,
|
|
|
|
|
parseValidObject,
|
|
|
|
|
stringify,
|
2024-07-22 11:49:05 +02:00
|
|
|
} from "../Utility/utility";
|
2026-09-26 23:32:38 +02:00
|
|
|
import {
|
|
|
|
|
checkMatchesObjectStructure,
|
|
|
|
|
DATA_VERSION,
|
|
|
|
|
ValidObject,
|
|
|
|
|
} from "../Utility/typeSafety";
|
2026-04-18 11:55:01 +00:00
|
|
|
import StorageModel, {
|
|
|
|
|
StorageModelSubPaths,
|
|
|
|
|
filePaths,
|
|
|
|
|
} from "../Global/storageModel";
|
2024-07-26 03:14:55 +02:00
|
|
|
import SettingsModel from "../Global/settingsModel";
|
2026-09-26 23:32:38 +02:00
|
|
|
import ChatModel, {
|
|
|
|
|
ChatInfoFileContent,
|
|
|
|
|
ChatInfoReference,
|
|
|
|
|
} from "../Chat/chatModel";
|
2024-07-21 21:45:18 +02:00
|
|
|
|
|
|
|
|
export default class FileModel {
|
2026-04-13 10:58:21 +00:00
|
|
|
readonly storageModel: StorageModel;
|
|
|
|
|
readonly settingsModel: SettingsModel;
|
2026-04-01 08:29:39 +00:00
|
|
|
|
2026-04-13 10:58:21 +00:00
|
|
|
readonly chatModel: ChatModel;
|
|
|
|
|
readonly boardsAndTasksModel: BoardsAndTasksModel;
|
2026-04-01 08:29:39 +00:00
|
|
|
|
|
|
|
|
// paths
|
2026-04-18 11:55:01 +00:00
|
|
|
get basePath(): string[] {
|
2026-04-01 08:29:39 +00:00
|
|
|
return StorageModel.getPath(
|
2026-04-08 14:02:12 +00:00
|
|
|
StorageModelSubPaths.Chat,
|
2026-04-01 08:29:39 +00:00
|
|
|
filePaths.chat.files(this.chatModel.id),
|
|
|
|
|
);
|
2026-08-29 02:10:26 +02:00
|
|
|
}
|
2026-04-01 08:29:39 +00:00
|
|
|
|
2026-04-18 11:55:01 +00:00
|
|
|
readonly getFileContainerPath = (): string[] => {
|
|
|
|
|
return [...this.basePath, FileModelSubPath.Data];
|
2026-04-01 08:29:39 +00:00
|
|
|
};
|
|
|
|
|
|
2026-08-29 02:10:26 +02:00
|
|
|
readonly getModelContainerPath = (
|
|
|
|
|
modelName: FileModelSubPath,
|
|
|
|
|
): string[] => {
|
2026-04-18 11:55:01 +00:00
|
|
|
return [...this.basePath, FileModelSubPath.Model, modelName];
|
2026-04-01 08:29:39 +00:00
|
|
|
};
|
|
|
|
|
|
2026-04-18 11:55:01 +00:00
|
|
|
readonly getFilePath = (fileId: string): string[] => {
|
2026-04-01 08:29:39 +00:00
|
|
|
return [...this.getFileContainerPath(), fileId];
|
|
|
|
|
};
|
|
|
|
|
|
2026-08-29 02:10:26 +02:00
|
|
|
readonly getFileContentPath = (
|
|
|
|
|
fileId: string,
|
|
|
|
|
fileContentId: string,
|
|
|
|
|
): string[] => {
|
2026-04-01 08:29:39 +00:00
|
|
|
const filePath: string[] = this.getFilePath(fileId);
|
|
|
|
|
return [...filePath, fileContentId];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// handlers
|
2026-08-29 02:10:26 +02:00
|
|
|
readonly handleStringifiedFileContent = (
|
|
|
|
|
stringifiedFileContent: string,
|
|
|
|
|
): void => {
|
2026-04-01 08:29:39 +00:00
|
|
|
const fileContent: FileContent<string> | null = parseValidObject(
|
|
|
|
|
stringifiedFileContent,
|
|
|
|
|
FileContentReference,
|
|
|
|
|
);
|
|
|
|
|
if (fileContent == null) return;
|
|
|
|
|
|
|
|
|
|
this.handleFileContent(fileContent);
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-18 11:55:01 +00:00
|
|
|
readonly handleFileContent = (fileContent: FileContent<string>): void => {
|
2026-09-26 23:32:38 +02:00
|
|
|
console.log(fileContent.type);
|
|
|
|
|
|
|
|
|
|
if (fileContent.type == "chat-info") {
|
|
|
|
|
if (
|
|
|
|
|
checkMatchesObjectStructure(fileContent, ChatInfoReference) ==
|
|
|
|
|
false
|
|
|
|
|
)
|
|
|
|
|
return;
|
|
|
|
|
this.chatModel.handleInfo(fileContent as ChatInfoFileContent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const didStore: boolean = this.storeFileContent(fileContent);
|
|
|
|
|
if (didStore == false) return;
|
|
|
|
|
|
|
|
|
|
switch (fileContent.type) {
|
|
|
|
|
case "board-info":
|
|
|
|
|
case "task":
|
|
|
|
|
this.boardsAndTasksModel.handleFileContent(fileContent);
|
|
|
|
|
break;
|
|
|
|
|
case "reaction":
|
|
|
|
|
this.chatModel.handleReaction(fileContent);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-04-01 08:29:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// methods
|
2026-08-29 02:10:26 +02:00
|
|
|
readonly addFileContentAndSend = (
|
2026-09-26 23:32:38 +02:00
|
|
|
fileContent: FileContent<string>,
|
2026-08-29 02:10:26 +02:00
|
|
|
): void => {
|
2026-09-26 23:32:38 +02:00
|
|
|
this.handleFileContent(fileContent);
|
|
|
|
|
this.chatModel.sendMessage("", undefined, fileContent);
|
2026-04-01 08:29:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// storage
|
2026-04-18 11:55:01 +00:00
|
|
|
readonly storeFileContent = (fileContent: FileContent<string>): boolean => {
|
2026-09-26 23:32:38 +02:00
|
|
|
const fileContentPath: string[] = this.getFileContentPath(
|
|
|
|
|
fileContent.fileId,
|
|
|
|
|
fileContent.fileContentId,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// check if fileContent already exists
|
|
|
|
|
const existingFileContent: string | null =
|
|
|
|
|
this.storageModel.read(fileContentPath);
|
|
|
|
|
if (existingFileContent != null) return false;
|
|
|
|
|
|
|
|
|
|
const stringifiedContent: string = stringify(fileContent);
|
|
|
|
|
this.storageModel.write(fileContentPath, stringifiedContent);
|
|
|
|
|
return true;
|
2026-04-01 08:29:39 +00:00
|
|
|
};
|
|
|
|
|
|
2026-04-18 11:55:01 +00:00
|
|
|
readonly listFileIds = (): string[] => {
|
2026-09-26 23:32:38 +02:00
|
|
|
return this.storageModel.list(this.basePath);
|
2026-04-01 08:29:39 +00:00
|
|
|
};
|
|
|
|
|
|
2026-04-18 11:55:01 +00:00
|
|
|
readonly listFileContentIds = (fileId: string): string[] => {
|
2026-09-26 23:32:38 +02:00
|
|
|
const filePath: string[] = this.getFilePath(fileId);
|
|
|
|
|
return this.storageModel.list(filePath);
|
2026-04-01 08:29:39 +00:00
|
|
|
};
|
|
|
|
|
|
2026-04-18 11:55:01 +00:00
|
|
|
readonly selectLatestFileContentId = (
|
2026-09-26 23:32:38 +02:00
|
|
|
fileContentIds: string[],
|
2026-04-01 08:29:39 +00:00
|
|
|
): string | undefined => {
|
2026-09-26 23:32:38 +02:00
|
|
|
return fileContentIds[fileContentIds.length - 1];
|
2026-04-01 08:29:39 +00:00
|
|
|
};
|
|
|
|
|
|
2026-04-18 11:55:01 +00:00
|
|
|
readonly getFileContent = <T extends FileContent<string>>(
|
2026-09-26 23:32:38 +02:00
|
|
|
fileId: string,
|
|
|
|
|
fileContentName: string,
|
|
|
|
|
reference: T,
|
2026-04-01 08:29:39 +00:00
|
|
|
): T | null => {
|
2026-09-26 23:32:38 +02:00
|
|
|
const filePath: string[] = this.getFileContentPath(
|
|
|
|
|
fileId,
|
|
|
|
|
fileContentName,
|
|
|
|
|
);
|
|
|
|
|
const fileContentOrNull: T | null = this.storageModel.readStringifiable(
|
|
|
|
|
filePath,
|
|
|
|
|
reference,
|
|
|
|
|
);
|
|
|
|
|
return fileContentOrNull;
|
2026-04-01 08:29:39 +00:00
|
|
|
};
|
|
|
|
|
|
2026-04-18 11:55:01 +00:00
|
|
|
readonly getLatestFileContent = <T extends FileContent<string>>(
|
2026-09-26 23:32:38 +02:00
|
|
|
fileId: string,
|
|
|
|
|
reference: T,
|
2026-04-01 08:29:39 +00:00
|
|
|
): T | null => {
|
2026-09-26 23:32:38 +02:00
|
|
|
const fileContentsIds: string[] = this.listFileContentIds(fileId);
|
|
|
|
|
const latestFileContentId: string | undefined =
|
|
|
|
|
this.selectLatestFileContentId(fileContentsIds);
|
|
|
|
|
if (latestFileContentId == undefined) return null;
|
|
|
|
|
|
|
|
|
|
const fileContent: T | null = this.getFileContent(
|
|
|
|
|
fileId,
|
|
|
|
|
latestFileContentId,
|
|
|
|
|
reference,
|
|
|
|
|
);
|
|
|
|
|
return fileContent;
|
2026-04-01 08:29:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// init
|
|
|
|
|
constructor(
|
2026-09-26 23:32:38 +02:00
|
|
|
storageModel: StorageModel,
|
|
|
|
|
settingsModel: SettingsModel,
|
|
|
|
|
chatModel: ChatModel,
|
2026-04-01 08:29:39 +00:00
|
|
|
) {
|
2026-09-26 23:32:38 +02:00
|
|
|
this.chatModel = chatModel;
|
|
|
|
|
this.settingsModel = settingsModel;
|
|
|
|
|
this.storageModel = storageModel;
|
|
|
|
|
|
|
|
|
|
this.boardsAndTasksModel = new BoardsAndTasksModel(
|
|
|
|
|
this.storageModel,
|
|
|
|
|
this.settingsModel,
|
|
|
|
|
chatModel,
|
|
|
|
|
this,
|
|
|
|
|
);
|
2026-04-01 08:29:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// utility
|
|
|
|
|
static generateFileContentId = (creationDate: string): string => {
|
2026-09-26 23:32:38 +02:00
|
|
|
return creationDate + v4();
|
2026-04-01 08:29:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static createFileContent = <T extends string>(
|
2026-09-26 23:32:38 +02:00
|
|
|
fileId: string,
|
|
|
|
|
type: T,
|
2026-04-01 08:29:39 +00:00
|
|
|
): FileContent<T> => {
|
2026-09-26 23:32:38 +02:00
|
|
|
const creationDate: string = createTimestamp();
|
|
|
|
|
const fileContentId: string =
|
|
|
|
|
FileModel.generateFileContentId(creationDate);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
dataVersion: DATA_VERSION,
|
|
|
|
|
|
|
|
|
|
fileId,
|
|
|
|
|
fileContentId,
|
|
|
|
|
creationDate,
|
|
|
|
|
type,
|
|
|
|
|
};
|
2026-04-01 08:29:39 +00:00
|
|
|
};
|
2024-07-21 20:54:18 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-22 12:39:26 +02:00
|
|
|
// paths
|
2024-07-23 18:23:26 +02:00
|
|
|
export enum FileModelSubPath {
|
2026-04-01 08:29:39 +00:00
|
|
|
Data = "data",
|
2026-09-26 23:32:38 +02:00
|
|
|
Model = "model",
|
|
|
|
|
Chat = "chat",
|
|
|
|
|
ModelView = "view",
|
|
|
|
|
ModelTask = "tasks",
|
|
|
|
|
ModelCalendar = "calendar",
|
2024-07-26 03:14:55 +02:00
|
|
|
}
|
2024-07-22 12:39:26 +02:00
|
|
|
|
2024-07-21 23:32:20 +02:00
|
|
|
// types
|
2024-07-26 03:14:55 +02:00
|
|
|
export interface FileContent<T extends string>
|
2026-04-01 08:29:39 +00:00
|
|
|
extends ValidObject, StringEntryObject {
|
2026-09-26 23:32:38 +02:00
|
|
|
readonly fileId: string;
|
|
|
|
|
readonly fileContentId: string;
|
|
|
|
|
readonly creationDate: string;
|
2024-07-21 23:32:20 +02:00
|
|
|
|
2026-09-26 23:32:38 +02:00
|
|
|
readonly type: T;
|
|
|
|
|
}
|
2024-07-22 01:22:59 +02:00
|
|
|
|
|
|
|
|
// references
|
2024-07-22 18:11:18 +02:00
|
|
|
export const FileContentReference: FileContent<string> = {
|
2026-04-01 08:29:39 +00:00
|
|
|
dataVersion: DATA_VERSION,
|
2024-07-22 01:22:59 +02:00
|
|
|
|
2026-04-01 08:29:39 +00:00
|
|
|
fileId: "",
|
|
|
|
|
fileContentId: "",
|
|
|
|
|
creationDate: "",
|
2024-07-22 01:22:59 +02:00
|
|
|
|
2026-04-01 08:29:39 +00:00
|
|
|
type: "",
|
2024-07-22 01:38:08 +02:00
|
|
|
};
|