build chatModel
This commit is contained in:
+3
-1
@@ -1,8 +1,10 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@types/uuid": "^10.0.0",
|
||||
"bloatless-react": "^1.3.8",
|
||||
"esbuild": "^0.23.0",
|
||||
"udn-frontend": "^1.0.7-beta1"
|
||||
"udn-frontend": "^1.0.7-beta1",
|
||||
"uuid": "^10.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "esbuild src/index.tsx --bundle --outdir=dist"
|
||||
|
||||
+118
-15
@@ -1,32 +1,134 @@
|
||||
// this file is responsible for managing chats and chat messages. objects are delegated to objectModel.ts.
|
||||
|
||||
import StorageModel, { storageKeys } from "./storageModel";
|
||||
import { createTimestamp, stringify } from "../utility";
|
||||
|
||||
import { createTimestamp } from "../utility";
|
||||
import { checkIsValidObject } from "../typeSafety";
|
||||
import { v4 } from "uuid";
|
||||
|
||||
export class ChatModel {
|
||||
// data
|
||||
id: string;
|
||||
storageModel: StorageModel;
|
||||
info: ChatInfo;
|
||||
|
||||
messages: ChatMessage[];
|
||||
objects: ChatObject[];
|
||||
outbox: ChatMessage[];
|
||||
messages = new Set<ChatMessage>();
|
||||
objects = new Map<string, ChatObject>();
|
||||
outbox = new Set<ChatMessage>();
|
||||
|
||||
// paths
|
||||
get infoPath(): string {
|
||||
return storageKeys.chatInfo(this.id);
|
||||
}
|
||||
|
||||
get messageDirPath(): string {
|
||||
return storageKeys.chatMessages(this.id);
|
||||
}
|
||||
|
||||
get objectDirPath(): string {
|
||||
return storageKeys.chatObjects(this.id);
|
||||
}
|
||||
|
||||
get outboxDirPath(): string {
|
||||
return storageKeys.chatOutbox(this.id);
|
||||
}
|
||||
|
||||
getMessagePath(id: string): string {
|
||||
return StorageModel.join(this.messageDirPath, id);
|
||||
}
|
||||
|
||||
getObjectPath(id: string): string {
|
||||
return StorageModel.join(this.objectDirPath, id);
|
||||
}
|
||||
|
||||
// store & add
|
||||
storeInfo(): void {
|
||||
this.storageModel.storeStringifiable(this.infoPath, this.info);
|
||||
}
|
||||
|
||||
addMessage(message: ChatMessage): void {
|
||||
if (!this.messages.has(message)) {
|
||||
this.messages.add(message);
|
||||
}
|
||||
const messagePath: string = this.getMessagePath(message.id);
|
||||
this.storageModel.storeStringifiable(messagePath, message);
|
||||
}
|
||||
|
||||
addObject(object: ChatObject): void {
|
||||
this.objects.set(object.id, object);
|
||||
const objectPath: string = this.getObjectPath(object.id);
|
||||
this.storageModel.storeStringifiable(objectPath, object);
|
||||
}
|
||||
|
||||
// restore
|
||||
restoreInfo(): void {
|
||||
const info: any = this.storageModel.restoreStringifiable(this.infoPath);
|
||||
this.info == info ?? generateChatInfo("0");
|
||||
}
|
||||
|
||||
restoreMessages(): void {
|
||||
const messageIds: string[] = this.storageModel.list(this.messageDirPath);
|
||||
if (!Array.isArray(messageIds)) return;
|
||||
|
||||
for (const messageId of messageIds) {
|
||||
const messagePath: string = this.getMessagePath(messageId);
|
||||
const message: any = this.storageModel.restoreStringifiable(messagePath);
|
||||
if (checkIsValidObject(message) == false) return;
|
||||
|
||||
this.messages.add(message);
|
||||
}
|
||||
}
|
||||
|
||||
restoreObjects(): void {
|
||||
const objectIds: string[] = this.storageModel.list(this.objectDirPath);
|
||||
if (!Array.isArray(objectIds)) return;
|
||||
|
||||
for (const objectId of objectIds) {
|
||||
const objectPath: string = this.getObjectPath(objectId);
|
||||
const object: any = this.storageModel.restoreStringifiable(objectPath);
|
||||
if (checkIsValidObject(object) == false) return;
|
||||
if (object.id != objectId) return;
|
||||
|
||||
this.objects.set(objectId, object);
|
||||
}
|
||||
}
|
||||
|
||||
// init
|
||||
constructor(storageModel: StorageModel, chatId: string) {
|
||||
const infoPath = storageKeys.chatInfo(chatId)
|
||||
const info = storageModel.restoreStringifiable(infoPath);
|
||||
this.id = chatId;
|
||||
this.storageModel = storageModel;
|
||||
|
||||
this.restoreInfo();
|
||||
this.restoreMessages();
|
||||
this.restoreObjects();
|
||||
}
|
||||
}
|
||||
|
||||
// creation methods
|
||||
export function createChatMessage(channel: string, sender: string, body: string): ChatMessage {
|
||||
export function generateChatInfo(primaryChannel: string): ChatInfo {
|
||||
return {
|
||||
primaryChannel,
|
||||
secondaryChannels: [],
|
||||
encryptionKey: "",
|
||||
hasUnreadMessages: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function createChatMessage(
|
||||
channel: string,
|
||||
sender: string,
|
||||
body: string
|
||||
): ChatMessage {
|
||||
return {
|
||||
dataVersion: "v2",
|
||||
|
||||
id: v4(),
|
||||
|
||||
channel,
|
||||
sender,
|
||||
body,
|
||||
dateSent: createTimestamp(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// types
|
||||
@@ -41,24 +143,26 @@ export interface ChatInfo {
|
||||
export interface ChatMessage {
|
||||
dataVersion: "v2";
|
||||
|
||||
id: string;
|
||||
|
||||
channel: string;
|
||||
sender: string;
|
||||
body: string;
|
||||
dateSent: string;
|
||||
|
||||
stringifiedObject?: string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ChatObject {
|
||||
export interface ChatObject {
|
||||
dataVersion: "v2";
|
||||
|
||||
id: string;
|
||||
title: string;
|
||||
|
||||
contentVersions: { [key: string]: MessageObjectContent };
|
||||
}
|
||||
contentVersions: { [key: string]: ChatObjectContent };
|
||||
}
|
||||
|
||||
export interface MessageObjectContent {
|
||||
export interface ChatObjectContent {
|
||||
dataVersion: "v2";
|
||||
|
||||
id: string;
|
||||
@@ -70,5 +174,4 @@ export interface ChatMessage {
|
||||
status?: string;
|
||||
date?: string;
|
||||
time?: string;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export default class ConnectionModel {
|
||||
|
||||
// messaging
|
||||
sendChatMessage = (chatMessage: ChatMessage): boolean => {
|
||||
const stringifiedBody = stringify(chatMessage);
|
||||
const stringifiedBody: string = stringify(chatMessage);
|
||||
return this.udn.sendMessage(chatMessage.channel, stringifiedBody);
|
||||
};
|
||||
|
||||
|
||||
+32
-21
@@ -2,13 +2,15 @@
|
||||
|
||||
import { parse, stringify } from "../utility";
|
||||
|
||||
import { DATA_VERSION } from "../typeSafety";
|
||||
|
||||
export default class StorageModel {
|
||||
storageEntryTree: StorageEntry = {};
|
||||
|
||||
// basic
|
||||
store = (key: string, value: string): void => {
|
||||
localStorage.setItem(key, value);
|
||||
const pathComponents = this.keyToPathComponents(key);
|
||||
const pathComponents: string[] = StorageModel.keyToPathComponents(key);
|
||||
this.updateTree(...pathComponents);
|
||||
};
|
||||
|
||||
@@ -21,9 +23,9 @@ export default class StorageModel {
|
||||
};
|
||||
|
||||
list = (key: string): string[] => {
|
||||
const pathComponents = this.keyToPathComponents(key);
|
||||
const pathComponents: string[] = StorageModel.keyToPathComponents(key);
|
||||
|
||||
let currentParent = this.storageEntryTree;
|
||||
let currentParent: StorageEntry = this.storageEntryTree;
|
||||
for (const component of pathComponents) {
|
||||
currentParent = currentParent[component];
|
||||
}
|
||||
@@ -32,13 +34,13 @@ export default class StorageModel {
|
||||
};
|
||||
|
||||
// stringifiable
|
||||
storeStringifiable = (key: string, value: any[]): void => {
|
||||
const valueString = stringify(value);
|
||||
storeStringifiable = (key: string, value: any): void => {
|
||||
const valueString: string = stringify(value);
|
||||
this.store(key, valueString);
|
||||
};
|
||||
|
||||
restoreStringifiable = (key: string): any | null => {
|
||||
const valueString = this.restore(key);
|
||||
const valueString: string | null = this.restore(key);
|
||||
if (!valueString) return null;
|
||||
|
||||
return parse(valueString);
|
||||
@@ -47,14 +49,14 @@ export default class StorageModel {
|
||||
// init
|
||||
constructor() {
|
||||
for (const key of Object.keys(localStorage)) {
|
||||
const components = this.keyToPathComponents(key);
|
||||
const components: string[] = StorageModel.keyToPathComponents(key);
|
||||
this.updateTree(...components);
|
||||
}
|
||||
}
|
||||
|
||||
// utility
|
||||
updateTree = (...pathComponents: string[]) => {
|
||||
let currentParent = this.storageEntryTree;
|
||||
let currentParent: StorageEntry = this.storageEntryTree;
|
||||
for (const pathPart of pathComponents) {
|
||||
if (!currentParent[pathPart]) {
|
||||
currentParent[pathPart] = {};
|
||||
@@ -64,13 +66,22 @@ export default class StorageModel {
|
||||
}
|
||||
};
|
||||
|
||||
pathComponentsToKey = (...pathComponents: string[]): string => {
|
||||
static pathComponentsToKey = (...pathComponents: string[]): string => {
|
||||
return pathComponents.join("/");
|
||||
};
|
||||
|
||||
keyToPathComponents = (key: string): string[] => {
|
||||
static keyToPathComponents = (key: string): string[] => {
|
||||
return key.split("/");
|
||||
};
|
||||
|
||||
static join = (...items: string[]): string => {
|
||||
let allComponents: string[] = [];
|
||||
for (const item of items) {
|
||||
const parts = this.keyToPathComponents(item);
|
||||
allComponents.push(...parts);
|
||||
}
|
||||
return StorageModel.pathComponentsToKey(...allComponents);
|
||||
};
|
||||
}
|
||||
|
||||
// types
|
||||
@@ -79,21 +90,21 @@ export type StorageEntry = { [key: string]: StorageEntry };
|
||||
// keys
|
||||
export const storageKeys = {
|
||||
// connection
|
||||
socketAddress: "v2/connection/socket-address",
|
||||
socketAddress: `${DATA_VERSION}/connection/socket-address`,
|
||||
|
||||
// settings
|
||||
userName: "v2/settings/user-name",
|
||||
firstDayOfWeek: "v2/settings/first-day-of-week",
|
||||
userName: `${DATA_VERSION}/settings/user-name`,
|
||||
firstDayOfWeek: `${DATA_VERSION}/settings/first-day-of-week`,
|
||||
|
||||
// history
|
||||
previousAddresses: "v2/history/previous-addresses",
|
||||
previousObjectCategories: "v2/history/object-categories",
|
||||
previousObjectStatuses: "v2/history/object-statuses",
|
||||
previousObjectFilters: "v2/history/object-filters",
|
||||
previousAddresses: `${DATA_VERSION}/history/previous-addresses`,
|
||||
previousObjectCategories: `${DATA_VERSION}/history/object-categories`,
|
||||
previousObjectStatuses: `${DATA_VERSION}/history/object-statuses`,
|
||||
previousObjectFilters: `${DATA_VERSION}/history/object-filters`,
|
||||
|
||||
// chat etc
|
||||
chatInfo: (id: string) => `v2/chat/${id}/info`,
|
||||
chatMessages: (id: string) => `v2/chat/${id}/messages`,
|
||||
chatObjects: (id: string) => `v2/chat/${id}/objects`,
|
||||
chatOutbox: (id: string) => `v2/chat/${id}/outbox`,
|
||||
chatInfo: (id: string) => `${DATA_VERSION}/chat/${id}/info`,
|
||||
chatMessages: (id: string) => `${DATA_VERSION}/chat/${id}/messages`,
|
||||
chatObjects: (id: string) => `${DATA_VERSION}/chat/${id}/objects`,
|
||||
chatOutbox: (id: string) => `${DATA_VERSION}/chat/${id}/outbox`,
|
||||
};
|
||||
|
||||
+8
-4
@@ -1,8 +1,10 @@
|
||||
import {
|
||||
ChatMessage,
|
||||
ChatObject,
|
||||
MessageObjectContent,
|
||||
} from "../Model/chatTypes";
|
||||
ChatObjectContent,
|
||||
} from "../Model/chatModel";
|
||||
|
||||
import { v4 } from "uuid";
|
||||
|
||||
// ChatMessage
|
||||
export interface ChatMessageV1 {
|
||||
@@ -21,6 +23,8 @@ export function upgradeMessageFromV1(
|
||||
return {
|
||||
dataVersion: "v2",
|
||||
|
||||
id: v4(),
|
||||
|
||||
channel,
|
||||
sender,
|
||||
body,
|
||||
@@ -41,7 +45,7 @@ export function upgradeMessageObjectFromV1(
|
||||
): ChatObject {
|
||||
const { id, title, contentVersions } = messageObjectV1;
|
||||
|
||||
const upgradedContentVersions: { [key: string]: MessageObjectContent } = {};
|
||||
const upgradedContentVersions: { [key: string]: ChatObjectContent } = {};
|
||||
for (const entry of Object.entries(contentVersions)) {
|
||||
const [id, object] = entry;
|
||||
const upgraded = upgradeMessageObjectContentFromV1(object);
|
||||
@@ -73,7 +77,7 @@ export interface MessageObjectContentV1 {
|
||||
|
||||
export function upgradeMessageObjectContentFromV1(
|
||||
messageObjectContentV1: MessageObjectContentV1
|
||||
): MessageObjectContent {
|
||||
): ChatObjectContent {
|
||||
const {
|
||||
isoDateVersionCreated,
|
||||
id,
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export const DATA_VERSION = "v2"
|
||||
|
||||
export function checkIsValidObject(object: any): boolean {
|
||||
return object.dataVersion == DATA_VERSION;
|
||||
}
|
||||
Reference in New Issue
Block a user