diff --git a/src/Model/Utility/typeSafety.ts b/src/Model/Utility/typeSafety.ts index c9becd2f..c8906471 100644 --- a/src/Model/Utility/typeSafety.ts +++ b/src/Model/Utility/typeSafety.ts @@ -3,3 +3,7 @@ export const DATA_VERSION = "v2" export function checkIsValidObject(object: any): boolean { return object.dataVersion == DATA_VERSION; } + +export interface ValidObject { + dataVersion: "v2"; +} \ No newline at end of file diff --git a/src/Model/chatModel.ts b/src/Model/chatModel.ts index b5eb74c6..c4f48728 100644 --- a/src/Model/chatModel.ts +++ b/src/Model/chatModel.ts @@ -2,6 +2,7 @@ import FileModel, { File } from "./fileModel"; import StorageModel, { storageKeys } from "./storageModel"; +import { ValidObject, checkIsValidObject } from "./Utility/typeSafety"; import { createTimestamp, parse, @@ -14,7 +15,6 @@ import ChatListModel from "./chatListModel"; import { Color } from "../ViewModel/colors"; import ConnectionModel from "./connectionModel"; import SettingsModel from "./settingsModel"; -import { checkIsValidObject } from "./Utility/typeSafety"; import { v4 } from "uuid"; export default class ChatModel { @@ -230,12 +230,14 @@ export default class ChatModel { connectionModel.setMessageSentHandler(this.handleMessageSent); - this.fileModel = new FileModel(this); + this.fileModel = new FileModel(this, this.storageModel); } // utility static generateChatInfo = (primaryChannel: string): ChatInfo => { return { + dataVersion: "v2", + primaryChannel, secondaryChannels: [], encryptionKey: "", @@ -281,7 +283,7 @@ export default class ChatModel { } // types -export interface ChatInfo { +export interface ChatInfo extends ValidObject { primaryChannel: string; secondaryChannels: string[]; encryptionKey: string; @@ -295,9 +297,7 @@ export enum ChatMessageStatus { Received = "received", } -export interface ChatMessage { - dataVersion: "v2"; - +export interface ChatMessage extends ValidObject { id: string; channel: string; diff --git a/src/Model/fileModel.ts b/src/Model/fileModel.ts index 3cb74fd2..74b581a7 100644 --- a/src/Model/fileModel.ts +++ b/src/Model/fileModel.ts @@ -1,11 +1,14 @@ // this file is responsible for managing files within chats. import ChatModel, { ChatMessage } from "./chatModel"; +import StorageModel, { storageKeys } from "./storageModel"; +import { parseValidObject, stringify } from "./Utility/utility"; -import { parseValidObject } from "./Utility/utility"; +import { ValidObject } from "./Utility/typeSafety"; export default class FileModel { chatModel: ChatModel; + storageModel: StorageModel; // handler handleStringifiedFile = (stringifiedFile: string): void => { @@ -18,25 +21,86 @@ export default class FileModel { this.chatModel.sendMessage("", file); }; + // storage + storeFile = (file: File): void => { + for (const fileContent of file.contentVersions) { + this.storeFileContent(file, fileContent); + } + }; + + storeFileContent = (file: File, fileContent: FileContent): void => { + const fileContentName: string = FileModel.getFileContentName(fileContent); + const fileContentPath: string[] = FileModel.getFileContentPath( + file, + fileContentName + ); + const stringifiedContent: string = stringify(fileContent); + this.storageModel.write(fileContentPath, stringifiedContent); + }; + + listFileIds = (): string[] => { + return this.storageModel.list(storageKeys.chatFiles); + }; + + listFileContents = (file: File): string[] => { + const filePath: string[] = FileModel.getFilePath(file.id); + return this.storageModel.list(filePath); + }; + + getFile = (fileId: string): File | null => { + const filePath = FileModel.getFilePath(fileId); + const fileOrNull: File | null = + this.storageModel.readStringifiable(filePath); + return fileOrNull; + }; + + getLatestFileContentName = (fileContentNames: string[]): string | null => { + return fileContentNames[fileContentNames.length - 1]; + }; + + getFileContent = ( + file: File, + fileContentName: string + ): FileContent | null => { + const filePath = FileModel.getFileContentPath(file, fileContentName); + const fileContentOrNull: FileContent | null = + this.storageModel.readStringifiable(filePath); + return fileContentOrNull; + }; + // init - constructor(chatModel: ChatModel) { + constructor(chatModel: ChatModel, storageModel: StorageModel) { this.chatModel = chatModel; + this.storageModel = storageModel; } + + // utility + static getFilePath = (fileId: string): string[] => { + return [...storageKeys.chatFiles, fileId]; + }; + + static getFileContentName = (fileContent: FileContent): string => { + return fileContent.creationDate + fileContent.id; + }; + + static getFileContentPath = ( + file: File, + fileContentName: string + ): string[] => { + const filePath: string[] = FileModel.getFilePath(file.id); + return [...filePath, fileContentName]; + }; } // types -export interface File { - dataVersion: "v2"; - +export interface File extends ValidObject { id: string; - title: string; - - contentVersions: { [id: string]: FileContent }; + contentVersions: FileContent[]; } -export interface FileContent { - dataVersion: "v2"; - +export interface FileContent extends ValidObject { id: string; creationDate: string; + + type: string; } diff --git a/src/Model/objectModel.ts b/src/Model/objectModel.ts new file mode 100644 index 00000000..83c998a9 --- /dev/null +++ b/src/Model/objectModel.ts @@ -0,0 +1 @@ +// this file is responsible for managing message objects.c \ No newline at end of file diff --git a/src/Model/storageModel.ts b/src/Model/storageModel.ts index 3a94f724..7866ec0b 100644 --- a/src/Model/storageModel.ts +++ b/src/Model/storageModel.ts @@ -1,8 +1,12 @@ // this file is responsible for reading and writing persistent data; all storage shall be handled by this file. -import { localeCompare, parse, stringify } from "./Utility/utility"; - -import { DATA_VERSION } from "./Utility/typeSafety"; +import { DATA_VERSION, ValidObject } from "./Utility/typeSafety"; +import { + localeCompare, + parse, + parseValidObject, + stringify, +} from "./Utility/utility"; export const PATH_COMPONENT_SEPARATOR = "\\"; @@ -69,16 +73,17 @@ export default class StorageModel { }; // stringifiable - writeStringifiable = (pathComponents: string[], value: any): void => { + writeStringifiable = (pathComponents: string[], value: ValidObject): void => { const valueString: string = stringify(value); this.write(pathComponents, valueString); }; - readStringifiable = (pathComponents: string[]): any | null => { + readStringifiable = (pathComponents: string[]): T | null => { const valueString: string | null = this.read(pathComponents); if (!valueString) return null; - return parse(valueString); + const object: T | null = parseValidObject(valueString); + return object; }; // init @@ -168,4 +173,5 @@ export const storageKeys = { ], chatColor: (id: string) => [DATA_VERSION, "chat", id, "color"], chatMessages: (id: string) => [DATA_VERSION, "chat", id, "messages"], + chatFiles: [DATA_VERSION, "chat", "files"], };