Files
CT/dist/index.js
T

4518 lines
161 KiB
JavaScript
Raw Normal View History

2024-07-07 13:43:18 +02:00
(() => {
2024-07-22 01:40:53 +02:00
// node_modules/uuid/dist/esm-browser/stringify.js
var byteToHex = [];
for (i = 0; i < 256; ++i) {
byteToHex.push((i + 256).toString(16).slice(1));
}
var i;
function unsafeStringify(arr, offset = 0) {
return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
}
// node_modules/uuid/dist/esm-browser/rng.js
var getRandomValues;
var rnds8 = new Uint8Array(16);
function rng() {
if (!getRandomValues) {
getRandomValues = typeof crypto !== "undefined" && crypto.getRandomValues && crypto.getRandomValues.bind(crypto);
if (!getRandomValues) {
throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
}
}
return getRandomValues(rnds8);
}
// node_modules/uuid/dist/esm-browser/native.js
var randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
var native_default = {
randomUUID
};
// node_modules/uuid/dist/esm-browser/v4.js
function v4(options, buf, offset) {
if (native_default.randomUUID && !buf && !options) {
return native_default.randomUUID();
}
options = options || {};
var rnds = options.random || (options.rng || rng)();
rnds[6] = rnds[6] & 15 | 64;
rnds[8] = rnds[8] & 63 | 128;
if (buf) {
offset = offset || 0;
for (var i = 0; i < 16; ++i) {
buf[offset + i] = rnds[i];
}
return buf;
}
return unsafeStringify(rnds);
}
var v4_default = v4;
// node_modules/bloatless-react/index.ts
var State = class {
_value;
_bindings = /* @__PURE__ */ new Set();
// init
constructor(initialValue) {
this._value = initialValue;
}
// value
get value() {
return this._value;
}
set value(newValue) {
if (this._value == newValue) return;
this._value = newValue;
this.callSubscriptions();
}
// subscriptions
callSubscriptions() {
this._bindings.forEach((fn) => fn(this._value));
}
subscribe(fn) {
this._bindings.add(fn);
fn(this._value);
}
subscribeSilent(fn) {
this._bindings.add(fn);
}
// stringify
toString() {
return JSON.stringify(this._value);
}
};
var ListState = class extends State {
additionHandlers = /* @__PURE__ */ new Set();
removalHandlers = /* @__PURE__ */ new Map();
// init
constructor(initialItems) {
super(new Set(initialItems));
}
// list
add(...items) {
items.forEach((item) => {
this.value.add(item);
this.additionHandlers.forEach((handler) => handler(item));
});
this.callSubscriptions();
}
remove(...items) {
items.forEach((item) => {
this.value.delete(item);
if (!this.removalHandlers.has(item)) return;
this.removalHandlers.get(item).forEach((handler) => handler(item));
this.removalHandlers.delete(item);
});
this.callSubscriptions();
}
clear() {
this.remove(...this.value.values());
}
// handlers
handleAddition(handler) {
this.additionHandlers.add(handler);
[...this.value.values()].forEach(handler);
}
handleRemoval(item, handler) {
if (!this.removalHandlers.has(item))
this.removalHandlers.set(item, /* @__PURE__ */ new Set());
this.removalHandlers.get(item).add(handler);
}
// stringification
toString() {
const array = [...this.value.values()];
const json = JSON.stringify(array);
return json;
}
};
var MapState = class extends State {
additionHandlers = /* @__PURE__ */ new Set();
removalHandlers = /* @__PURE__ */ new Map();
// init
constructor(initialItems) {
super(new Map(initialItems));
}
// list
set(key, item) {
this.remove(key);
this.value.set(key, item);
this.additionHandlers.forEach((handler) => handler(item));
this.callSubscriptions();
}
remove(key) {
const item = this.value.get(key);
if (!item) return;
this.value.delete(key);
this.callSubscriptions();
if (!this.removalHandlers.has(item)) return;
this.removalHandlers.get(item).forEach((handler) => handler(item));
this.removalHandlers.delete(item);
}
clear() {
[...this.value.keys()].forEach((key) => this.remove(key));
}
// handlers
handleAddition(handler) {
this.additionHandlers.add(handler);
[...this.value.values()].forEach(handler);
}
handleRemoval(item, handler) {
if (!this.removalHandlers.has(item))
this.removalHandlers.set(item, /* @__PURE__ */ new Set());
this.removalHandlers.get(item).add(handler);
}
// stringification
toString() {
const array = [...this.value.entries()];
const json = JSON.stringify(array);
return json;
}
};
function createProxyState(statesToSubscibe, fn) {
const proxyState = new State(fn());
statesToSubscibe.forEach(
(state) => state.subscribe(() => proxyState.value = fn())
);
return proxyState;
}
2024-07-26 03:14:55 +02:00
function bulkSubscribe(statesToSubscibe, fn) {
statesToSubscibe.forEach((state) => state.subscribeSilent(fn));
}
2024-07-22 01:40:53 +02:00
function createElement(tagName, attributes = {}, ...children) {
const element = document.createElement(tagName);
if (attributes != null)
Object.entries(attributes).forEach((entry) => {
const [attributename, value] = entry;
const [directiveKey, directiveValue] = attributename.split(":");
switch (directiveKey) {
case "on": {
switch (directiveValue) {
case "enter": {
element.addEventListener("keydown", (e) => {
if (e.key != "Enter") return;
value();
});
break;
}
default: {
element.addEventListener(directiveValue, value);
}
}
break;
}
case "subscribe": {
const state = value;
state.subscribe(
(newValue) => element[directiveValue] = newValue
);
break;
}
case "bind": {
const state = value;
state.subscribe(
(newValue) => element[directiveValue] = newValue
);
element.addEventListener(
"input",
() => state.value = element[directiveValue]
);
break;
}
case "toggle": {
if (value.subscribe) {
const state = value;
state.subscribe(
(newValue) => element.toggleAttribute(directiveValue, newValue)
);
} else {
element.toggleAttribute(directiveValue, value);
}
break;
}
case "set": {
const state = value;
state.subscribe(
(newValue) => element.setAttribute(directiveValue, newValue)
);
break;
}
case "children": {
switch (directiveValue) {
case "set": {
const state = value;
state.subscribe((newValue) => {
element.innerHTML = "";
element.append(...[newValue].flat());
});
break;
}
case "append":
case "prepend": {
try {
const [listState, toElement] = value;
listState.handleAddition((newItem) => {
const child = toElement(newItem);
listState.handleRemoval(
newItem,
() => child.remove()
);
if (directiveValue == "append") {
element.append(child);
} else if (directiveValue == "prepend") {
element.prepend(child);
}
});
} catch (error) {
2024-07-23 00:05:48 +02:00
console.error(error);
2024-07-22 01:40:53 +02:00
throw `error: cannot process subscribe:children directive.
2024-07-23 00:05:48 +02:00
Usage: "children:append={[list, converter]}"; you can find a more detailed example in the documentation.`;
2024-07-22 01:40:53 +02:00
}
}
}
break;
}
default:
element.setAttribute(attributename, value);
}
});
children.filter((x) => x).forEach((child) => element.append(child));
return element;
}
// src/Model/Utility/typeSafety.ts
var DATA_VERSION = "v2";
function checkIsValidObject(object) {
return object.dataVersion == DATA_VERSION;
}
2024-07-22 01:40:53 +02:00
function checkMatchesObjectStructure(objectToCheck, reference) {
if (typeof reference != "object") {
return typeof objectToCheck == typeof reference;
}
for (const key of Object.keys(reference)) {
const requiredType = typeof reference[key];
const actualType = typeof objectToCheck[key];
if (requiredType != actualType) return false;
2024-07-22 11:49:05 +02:00
if (Array.isArray(reference[key]) || reference[key] instanceof Set) {
2024-07-22 01:40:53 +02:00
if (objectToCheck[key].length == 0) continue;
2024-07-22 11:49:05 +02:00
if (objectToCheck[key].size == 0) continue;
const [firstOfObjectToCheck] = objectToCheck[key];
const [fisrtOfReference] = reference[key];
2024-07-22 01:40:53 +02:00
const doesFirstItemMatch = checkMatchesObjectStructure(
2024-07-22 11:49:05 +02:00
firstOfObjectToCheck,
fisrtOfReference
2024-07-22 01:40:53 +02:00
);
if (doesFirstItemMatch == false) return false;
} else if (requiredType == "object") {
const doesNestedObjectMatch = checkMatchesObjectStructure(
objectToCheck[key],
reference[key]
);
if (doesNestedObjectMatch == false) return false;
}
}
return true;
}
2024-07-20 05:10:50 +02:00
// src/Model/Utility/utility.ts
2024-07-20 21:10:06 +02:00
function createTimestamp() {
return (/* @__PURE__ */ new Date()).toISOString();
}
2024-07-25 02:49:23 +02:00
function checkDoesObjectMatchReference(reference, stringEntryObject) {
reference_entry_loop: for (const referenceEntry of Object.entries(
reference
)) {
const [referenceKey, referenceValue] = referenceEntry;
const stringEntryObjectValue = stringEntryObject[referenceKey];
if (referenceValue == void 0) return false;
if (referenceValue[0] == "-") {
const strippedReferenceValue = referenceValue.substring(1);
if (strippedReferenceValue == "" && stringEntryObjectValue != void 0 && stringEntryObjectValue != "") {
return false;
}
if (stringEntryObjectValue == strippedReferenceValue) {
return false;
}
} else {
if (referenceValue == "" && (stringEntryObjectValue == void 0 || stringEntryObjectValue == "")) {
return false;
} else if (referenceValue == "") {
continue reference_entry_loop;
}
if (stringEntryObjectValue != referenceValue) {
return false;
}
}
}
return true;
}
function collectObjectValuesForKey(key, converter, objects) {
const values = /* @__PURE__ */ new Set();
for (const object of objects) {
const stringEntryObject = converter(object);
const stringEntryObjectValue = stringEntryObject[key];
if (stringEntryObjectValue == void 0 || stringEntryObjectValue == "")
continue;
values.add(stringEntryObjectValue);
}
2024-07-25 18:25:05 +02:00
return [...values.values()];
2024-07-25 02:49:23 +02:00
}
2024-07-25 15:31:27 +02:00
function checkDoesObjectMatchSearch(query, getStringsOfObject, object) {
if (query == "") return true;
const stringsInObject = getStringsOfObject(object);
const wordsInObject = [];
for (const string of stringsInObject) {
const lowercaseWordsInString = string.toLocaleLowerCase().split(" ").filter((word) => word != "");
wordsInObject.push(...lowercaseWordsInString);
}
const lowercaseWordsInQuery = query.toLowerCase().split(" ").filter((word) => word != "");
for (const queryWord of lowercaseWordsInQuery) {
if (queryWord[0] == "-") {
const wordContent = queryWord.substring(1);
if (wordsInObject.includes(wordContent)) {
return false;
}
} else {
if (wordsInObject.includes(queryWord) == false) {
return false;
}
}
}
return true;
}
2024-07-25 14:15:00 +02:00
var HandlerManager = class {
handlers = /* @__PURE__ */ new Set();
// manage
addHandler = (handler) => {
this.handlers.add(handler);
};
deleteHandler = (handler) => {
this.handlers.delete(handler);
};
// trigger
trigger = (item) => {
for (const handler of this.handlers) {
handler(item);
}
};
};
2024-07-23 14:15:01 +02:00
var IndexManager = class {
itemToString;
sortedStrings = [];
// methods
update = (items) => {
this.sortedStrings = [];
let strings = [];
for (const item of items) {
const string = this.itemToString(item);
strings.push(string);
}
this.sortedStrings = strings.sort(localeCompare);
};
getIndex = (item) => {
const string = this.itemToString(item);
const index = this.sortedStrings.indexOf(string);
return index;
};
// init
constructor(itemToString) {
this.itemToString = itemToString;
}
};
2024-07-20 05:10:50 +02:00
function stringify(data) {
2024-07-20 20:42:43 +02:00
return JSON.stringify(data, null, 4);
2024-07-20 05:10:50 +02:00
}
2024-07-24 14:34:28 +02:00
function padZero(string, length) {
return (string ?? "").padStart(length, "0");
}
2024-07-20 05:10:50 +02:00
function parse(string) {
try {
return JSON.parse(string);
} catch {
return {};
}
}
2024-07-22 01:40:53 +02:00
function parseValidObject(string, reference) {
const parsed = parse(string);
if (checkIsValidObject(parsed) == false) return null;
2024-07-22 01:40:53 +02:00
const doesMatchReference = checkMatchesObjectStructure(
parsed,
reference
);
if (doesMatchReference == false) return null;
return parsed;
2024-07-20 05:10:50 +02:00
}
2024-07-20 18:58:25 +02:00
function localeCompare(a, b) {
return a.localeCompare(b);
}
2024-07-20 05:10:50 +02:00
2024-07-22 02:05:27 +02:00
// src/Model/Global/storageModel.ts
2024-07-20 05:10:50 +02:00
var PATH_COMPONENT_SEPARATOR = "\\";
var StorageModel = class _StorageModel {
storageEntryTree = {};
2024-07-22 15:06:01 +02:00
// read
read = (pathComponents) => {
2024-07-21 17:55:53 +02:00
const pathString = _StorageModel.pathComponentsToString(
...pathComponents
);
2024-07-22 15:06:01 +02:00
return localStorage.getItem(pathString);
2024-07-20 05:10:50 +02:00
};
2024-07-22 15:06:01 +02:00
list = (pathComponents) => {
let currentParent = this.storageEntryTree;
for (const component of pathComponents) {
const nextParent = currentParent[component];
if (nextParent == void 0) return [];
currentParent = nextParent;
}
return [...Object.keys(currentParent).sort(localeCompare)];
};
// write
write = (pathComponents, value) => {
2024-07-21 17:55:53 +02:00
const pathString = _StorageModel.pathComponentsToString(
...pathComponents
);
2024-07-22 15:06:01 +02:00
localStorage.setItem(pathString, value);
this.updateTree(...pathComponents);
2024-07-20 05:10:50 +02:00
};
remove = (pathComponents, shouldInitialize = true) => {
2024-07-21 17:55:53 +02:00
const pathString = _StorageModel.pathComponentsToString(
...pathComponents
);
localStorage.removeItem(pathString);
2024-07-20 05:10:50 +02:00
if (shouldInitialize == true) {
this.initializeTree();
}
};
2024-07-22 15:06:01 +02:00
removeRecursively = (pathComponents) => {
loop_over_files: for (const key of Object.keys(localStorage)) {
2024-07-21 17:55:53 +02:00
const pathComponentsOfCurrentEntity = _StorageModel.stringToPathComponents(key);
2024-07-22 15:06:01 +02:00
loop_over_path_components: for (let i = 0; i < pathComponents.length; i++) {
if (!pathComponentsOfCurrentEntity[i]) continue loop_over_files;
if (pathComponentsOfCurrentEntity[i] != pathComponents[i])
continue loop_over_files;
2024-07-20 05:10:50 +02:00
}
this.remove(pathComponentsOfCurrentEntity, false);
}
this.initializeTree();
};
2024-07-22 15:06:01 +02:00
rename = (sourcePathComponents, destinationPathComponents, shouldInitialize = true) => {
2024-07-22 13:09:33 +02:00
const content = this.read(sourcePathComponents);
if (content == null) return false;
this.write(destinationPathComponents, content);
this.remove(sourcePathComponents);
2024-07-22 15:06:01 +02:00
if (shouldInitialize == true) {
this.initializeTree();
}
2024-07-22 13:09:33 +02:00
return true;
};
2024-07-22 15:06:01 +02:00
renameRecursively = (sourcePathComponents, destinationPathComponents) => {
loop_over_files: for (const key of Object.keys(localStorage)) {
const originalPathComponentsOfCurrentEntity = _StorageModel.stringToPathComponents(key);
loop_over_path_components: for (let i = 0; i < sourcePathComponents.length; i++) {
if (!originalPathComponentsOfCurrentEntity[i]) continue loop_over_files;
if (originalPathComponentsOfCurrentEntity[i] != sourcePathComponents[i])
continue loop_over_files;
}
const relativePathOfCurrentEntity = originalPathComponentsOfCurrentEntity.slice(
sourcePathComponents.length
);
const destinationPathComponentsOfCurrentEntity = [
...destinationPathComponents,
...relativePathOfCurrentEntity
];
this.rename(
originalPathComponentsOfCurrentEntity,
destinationPathComponentsOfCurrentEntity,
false
);
}
this.initializeTree();
};
2024-07-20 05:10:50 +02:00
// stringifiable
2024-07-21 21:45:18 +02:00
writeStringifiable = (pathComponents, value) => {
2024-07-20 05:10:50 +02:00
const valueString = stringify(value);
2024-07-21 21:45:18 +02:00
this.write(pathComponents, valueString);
2024-07-20 05:10:50 +02:00
};
2024-07-22 01:40:53 +02:00
readStringifiable = (pathComponents, reference) => {
2024-07-21 21:45:18 +02:00
const valueString = this.read(pathComponents);
2024-07-20 05:10:50 +02:00
if (!valueString) return null;
2024-07-22 01:40:53 +02:00
const object = parseValidObject(valueString, reference);
if (object == null) return null;
2024-07-21 23:37:54 +02:00
return object;
2024-07-20 05:10:50 +02:00
};
2024-07-22 03:01:04 +02:00
// tree
2024-07-20 05:10:50 +02:00
initializeTree = () => {
console.log("initializing tree");
this.storageEntryTree = {};
for (const key of Object.keys(localStorage)) {
2024-07-21 17:55:53 +02:00
const components = _StorageModel.stringToPathComponents(key);
2024-07-20 05:10:50 +02:00
this.updateTree(...components);
}
};
updateTree = (...pathComponents) => {
let currentParent = this.storageEntryTree;
for (const pathPart of pathComponents) {
if (!currentParent[pathPart]) {
currentParent[pathPart] = {};
}
currentParent = currentParent[pathPart];
}
};
2024-07-21 17:55:53 +02:00
printTree = () => {
return stringify(this.storageEntryTree);
2024-07-20 05:10:50 +02:00
};
2024-07-22 03:01:04 +02:00
// init
constructor() {
this.initializeTree();
}
// utility
2024-07-21 18:56:56 +02:00
static getFileName = (pathComponents) => {
return pathComponents[pathComponents.length - 1] || "\\";
};
static getFileNameFromString = (pathString) => {
const pathComponents = this.stringToPathComponents(pathString);
return pathComponents[pathComponents.length - 1] || "\\";
};
2024-07-21 17:55:53 +02:00
static pathComponentsToString = (...pathComponents) => {
2024-07-22 15:06:01 +02:00
return pathComponents.filter((x) => x != "").join(PATH_COMPONENT_SEPARATOR);
2024-07-20 05:10:50 +02:00
};
2024-07-21 17:55:53 +02:00
static stringToPathComponents = (string) => {
return string.split(PATH_COMPONENT_SEPARATOR).filter((x) => x != "");
2024-07-20 05:10:50 +02:00
};
static join = (...items) => {
let allComponents = [];
for (const item of items) {
2024-07-21 17:55:53 +02:00
const parts = this.stringToPathComponents(item);
2024-07-20 05:10:50 +02:00
allComponents.push(...parts);
}
2024-07-21 17:55:53 +02:00
return _StorageModel.pathComponentsToString(...allComponents);
2024-07-20 05:10:50 +02:00
};
2024-07-22 03:01:04 +02:00
static getPath(locationName, filePath) {
2024-07-25 16:44:08 +02:00
return [DATA_VERSION, locationName, ...filePath];
2024-07-22 03:01:04 +02:00
}
};
var filePaths = {
connectionModel: {
socketAddress: ["socket-address"],
reconnectAddress: ["reconnect-address"],
outbox: ["outbox"],
mailboxes: ["mailboxes"],
previousAddresses: ["previous-addresses"]
},
chat: {
base: [],
chatBase: (id) => [id],
info: (id) => [...filePaths.chat.chatBase(id), "info"],
color: (id) => [...filePaths.chat.chatBase(id), "color"],
messages: (id) => [...filePaths.chat.chatBase(id), "messages"],
lastUsedPage: (id) => [
...filePaths.chat.chatBase(id),
"last-used-page"
2024-07-22 13:09:33 +02:00
],
files: (id) => [...filePaths.chat.chatBase(id), "files"]
2024-07-22 03:01:04 +02:00
},
settingsModel: {
username: ["user-name"],
firstDayOfWeek: ["first-day-of-week"]
}
2024-07-20 05:10:50 +02:00
};
2024-07-22 01:31:47 +02:00
2024-07-26 03:14:55 +02:00
// src/Model/Files/calendarModel.ts
var CalendarModel = class _CalendarModel {
storageModel;
fileModel;
settingsModel;
// paths
getBasePath = () => {
return this.fileModel.getModelContainerPath("calendar" /* ModelCalendar */);
};
getViewPath = () => {
return [...this.getBasePath(), "view" /* ModelView */];
};
getMonthContainerPath = () => {
return [...this.getBasePath(), "months" /* Months */];
};
getMonthPath = (monthString) => {
return [...this.getMonthContainerPath(), monthString];
};
// main
storeTaskReference = (taskFileContent) => {
if (taskFileContent.date == void 0) return;
const monthString = _CalendarModel.isoToMonthString(
taskFileContent.date
);
const monthPath = this.getMonthPath(monthString);
const referencePath = [...monthPath, taskFileContent.fileId];
this.storageModel.write(referencePath, "");
};
listTaskIds = (monthString) => {
const monthPath = this.getMonthPath(monthString);
return this.storageModel.list(monthPath);
};
generateMonthGrid = (year, month, defaultValueCreator) => {
const date = /* @__PURE__ */ new Date();
date.setFullYear(year);
date.setMonth(month - 1);
date.setDate(1);
const offset = date.getDay() - parseInt(this.settingsModel.firstDayOfWeek);
date.setMonth(month);
date.setDate(-1);
const daysInMonth = date.getDate() + 1;
const grid = {
offset,
2024-07-26 03:29:07 +02:00
firstDayOfWeek: parseInt(this.settingsModel.firstDayOfWeek),
2024-07-26 03:14:55 +02:00
days: {}
};
for (let i = 0; i < daysInMonth; i++) {
grid.days[i + 1] = defaultValueCreator();
}
return grid;
};
// init
constructor(storageModel2, settingsModel2, fileModel) {
this.storageModel = storageModel2;
this.settingsModel = settingsModel2;
this.fileModel = fileModel;
}
// utility
static isoToMonthString = (dateISOString) => {
const [year, month, _] = dateISOString.split("-");
return _CalendarModel.getMonthString(year, month);
};
static isoToDateString = (dateISOString) => {
const [year, month, date, _] = dateISOString.split("-");
return date;
};
static getMonthString = (year = "", month = "") => {
const paddedYear = year.padStart(4, "0");
const paddedMonth = month.padStart(2, "0");
return `${paddedYear}-${paddedMonth}`;
};
};
2024-07-22 21:07:44 +02:00
// src/colors.ts
2024-07-22 14:36:03 +02:00
var Color = /* @__PURE__ */ ((Color2) => {
Color2["Standard"] = "standard";
Color2["Coral"] = "coral";
Color2["Yellow"] = "yellow";
Color2["Mint"] = "mint";
Color2["LightBlue"] = "lightblue";
Color2["Blue"] = "blue";
Color2["purple"] = "purple";
return Color2;
})(Color || {});
// src/Model/Files/boardsAndTasksModel.ts
var BoardsAndTasksModel = class _BoardsAndTasksModel {
2024-07-22 13:09:33 +02:00
storageModel;
2024-07-26 03:14:55 +02:00
settingsModel;
2024-07-22 03:01:04 +02:00
chatModel;
fileModel;
2024-07-26 03:14:55 +02:00
calendarModel;
2024-07-23 18:51:41 +02:00
// data
boardHandlerManager = new HandlerManager();
2024-07-24 13:40:17 +02:00
taskHandlerManager = new HandlerManager();
2024-07-22 13:09:33 +02:00
// paths
getBasePath = () => {
2024-07-23 18:23:26 +02:00
return this.fileModel.getModelContainerPath("tasks" /* ModelTask */);
2024-07-22 13:09:33 +02:00
};
getViewPath = () => {
return [...this.getBasePath(), "view" /* ModelView */];
};
getBoardFilePath = (boardId) => {
2024-07-22 18:09:58 +02:00
return [...this.fileModel.getFilePath(boardId)];
2024-07-22 13:09:33 +02:00
};
2024-07-22 19:28:39 +02:00
getTaskFilePath = (taskId) => {
return [...this.fileModel.getFilePath(taskId)];
};
getBoardContainerPath = () => {
2024-07-23 18:23:26 +02:00
return [...this.getBasePath(), "boards" /* Boards */];
2024-07-22 13:09:33 +02:00
};
getBoardDirectoryPath = (boardId) => {
return [...this.getBoardContainerPath(), boardId];
2024-07-22 13:17:21 +02:00
};
getTaskContainerPath = (boardId) => {
2024-07-23 18:23:26 +02:00
return [
...this.getBoardDirectoryPath(boardId),
"tasks" /* BoardTasks */
];
2024-07-22 11:49:05 +02:00
};
2024-07-22 19:28:39 +02:00
getTaskReferencePath = (boardId, fileId) => {
return [...this.getTaskContainerPath(boardId), fileId];
2024-07-22 11:49:05 +02:00
};
2024-07-22 23:33:57 +02:00
// handlers
2024-07-22 18:11:18 +02:00
handleFileContent = (fileContent) => {
if (checkMatchesObjectStructure(fileContent, BoardInfoFileContentReference) == true) {
this.handleBoard(fileContent);
} else if (checkMatchesObjectStructure(fileContent, TaskFileContentReference) == true) {
this.handleTask(fileContent);
2024-07-22 14:36:03 +02:00
}
2024-07-22 13:09:33 +02:00
};
2024-07-22 18:11:18 +02:00
handleBoard = (boardInfoFileContent) => {
2024-07-23 01:40:30 +02:00
this.updateBoard(boardInfoFileContent);
};
2024-07-22 18:11:18 +02:00
handleTask = (taskFileContent) => {
2024-07-24 13:40:17 +02:00
this.updateTask(taskFileContent);
};
2024-07-22 14:36:03 +02:00
// boards
createBoard = (name) => {
const boardInfoFileContent = _BoardsAndTasksModel.createBoardInfoFileContent(
v4_default(),
name,
"standard" /* Standard */
);
2024-07-23 00:32:29 +02:00
return boardInfoFileContent;
2024-07-22 13:17:21 +02:00
};
2024-07-22 19:28:39 +02:00
updateBoard = (boardInfoFileContent) => {
this.storeBoard(boardInfoFileContent);
this.boardHandlerManager.trigger(boardInfoFileContent);
2024-07-22 13:17:21 +02:00
};
2024-07-24 13:40:17 +02:00
updateBoardAndSend = (boardInfoFileContent) => {
this.updateBoard(boardInfoFileContent);
this.chatModel.sendMessage("", boardInfoFileContent);
};
2024-07-22 19:28:39 +02:00
storeBoard = (boardInfoFileContent) => {
this.fileModel.storeFileContent(boardInfoFileContent);
const boardDirectoryPath = this.getBoardDirectoryPath(
2024-07-22 18:11:18 +02:00
boardInfoFileContent.fileId
);
this.storageModel.write(boardDirectoryPath, "");
2024-07-22 15:06:01 +02:00
};
2024-07-22 18:09:58 +02:00
deleteBoard = (boardId) => {
const boardFilePath = this.getBoardFilePath(boardId);
const boardDirectoryPath = this.getBoardDirectoryPath(boardId);
this.storageModel.removeRecursively(boardFilePath);
this.storageModel.removeRecursively(boardDirectoryPath);
};
2024-07-22 19:28:39 +02:00
listBoardIds = () => {
const boardContainerPath = this.getBoardContainerPath();
2024-07-23 00:32:29 +02:00
const boardIds = this.storageModel.list(boardContainerPath);
return boardIds;
2024-07-22 19:28:39 +02:00
};
getBoardInfo = (fileId) => {
const boardInfoFileContentOrNull = this.fileModel.getLatestFileContent(
fileId,
BoardInfoFileContentReference
);
return boardInfoFileContentOrNull;
};
2024-07-22 14:36:03 +02:00
//tasks
2024-07-24 01:19:44 +02:00
createTask = (boardId) => {
const taskFileContent = _BoardsAndTasksModel.createTaskFileContent(v4_default(), "", boardId);
2024-07-24 01:19:44 +02:00
return taskFileContent;
2024-07-22 19:28:39 +02:00
};
updateTask = (taskFileContent) => {
this.storeTask(taskFileContent);
2024-07-24 13:40:17 +02:00
this.taskHandlerManager.trigger(taskFileContent);
};
updateTaskAndSend = (taskFileContent) => {
this.updateTask(taskFileContent);
this.chatModel.sendMessage("", taskFileContent);
2024-07-22 19:28:39 +02:00
};
storeTask = (taskFileContent) => {
this.fileModel.storeFileContent(taskFileContent);
2024-07-22 19:28:39 +02:00
const taskReferencePath = this.getTaskReferencePath(
taskFileContent.boardId,
taskFileContent.fileId
);
this.storageModel.write(taskReferencePath, "");
2024-07-26 03:14:55 +02:00
this.calendarModel.storeTaskReference(taskFileContent);
2024-07-22 14:36:03 +02:00
};
2024-07-22 19:28:39 +02:00
listTaskIds = (boardId) => {
const taskContainerPath = this.getTaskContainerPath(boardId);
const fileIds = this.storageModel.list(taskContainerPath);
2024-07-22 13:17:21 +02:00
return fileIds;
};
2024-07-24 15:57:02 +02:00
listTaskVersionIds = (taskId) => {
const versionIds = this.fileModel.listFileContentIds(taskId);
return versionIds;
};
getLatestTaskFileContent = (taskId) => {
2024-07-24 01:19:44 +02:00
const taskFileContentOrNull = this.fileModel.getLatestFileContent(taskId, TaskFileContentReference);
return taskFileContentOrNull;
};
2024-07-24 15:57:02 +02:00
getSpecificTaskFileContent = (taskId, versionId) => {
const taskFileContentOrNull = this.fileModel.getFileContent(
taskId,
versionId,
TaskFileContentReference
);
return taskFileContentOrNull;
};
2024-07-22 19:28:39 +02:00
deleteTask = (boardId, taskId) => {
const taskFilePath = this.getTaskFilePath(taskId);
this.storageModel.removeRecursively(taskFilePath);
this.deleteTaskReference(boardId, taskId);
};
deleteTaskReference = (boardId, taskId) => {
2024-07-22 19:28:39 +02:00
const taskReferencePath = this.getTaskReferencePath(
boardId,
taskId
);
this.storageModel.removeRecursively(taskReferencePath);
2024-07-22 14:36:03 +02:00
};
2024-07-22 03:01:04 +02:00
// init
2024-07-26 03:14:55 +02:00
constructor(storageModel2, settingsModel2, chatModel, fileModel) {
this.storageModel = storageModel2;
this.settingsModel = settingsModel2;
2024-07-22 03:01:04 +02:00
this.chatModel = chatModel;
this.fileModel = fileModel;
2024-07-26 03:14:55 +02:00
this.calendarModel = new CalendarModel(
this.storageModel,
this.settingsModel,
this.fileModel
);
2024-07-22 03:01:04 +02:00
}
2024-07-22 11:49:05 +02:00
// utility
2024-07-22 18:11:18 +02:00
static createBoardInfoFileContent = (fileId, name, color) => {
2024-07-26 03:14:55 +02:00
const fileContent = FileModel2.createFileContent(
fileId,
"board-info"
);
2024-07-22 14:36:03 +02:00
return {
2024-07-22 18:11:18 +02:00
...fileContent,
2024-07-22 14:36:03 +02:00
name,
color
};
};
2024-07-22 18:11:18 +02:00
static createTaskFileContent = (fileId, name, boardId) => {
2024-07-26 03:14:55 +02:00
const fileContent = FileModel2.createFileContent(
fileId,
"task"
);
2024-07-22 11:49:05 +02:00
return {
2024-07-22 18:11:18 +02:00
...fileContent,
2024-07-22 11:49:05 +02:00
name,
2024-07-22 14:36:03 +02:00
boardId
2024-07-22 11:49:05 +02:00
};
};
};
2024-07-22 18:11:18 +02:00
var BoardInfoFileContentReference = {
2024-07-22 14:36:03 +02:00
dataVersion: DATA_VERSION,
fileId: "string",
2024-07-22 18:11:18 +02:00
fileContentId: "",
2024-07-22 14:36:03 +02:00
creationDate: "",
type: "board-info",
2024-07-22 14:36:03 +02:00
name: "",
color: ""
2024-07-22 13:09:33 +02:00
};
2024-07-22 18:11:18 +02:00
var TaskFileContentReference = {
2024-07-22 11:49:05 +02:00
dataVersion: DATA_VERSION,
fileId: "string",
2024-07-22 18:11:18 +02:00
fileContentId: "",
2024-07-22 11:49:05 +02:00
creationDate: "",
type: "task",
name: "",
2024-07-22 14:36:03 +02:00
boardId: ""
2024-07-22 03:01:04 +02:00
};
2024-07-22 02:05:27 +02:00
// src/Model/Files/fileModel.ts
2024-07-26 03:14:55 +02:00
var FileModel2 = class _FileModel {
2024-07-21 23:37:54 +02:00
storageModel;
2024-07-26 03:14:55 +02:00
settingsModel;
chatModel;
boardsAndTasksModel;
2024-07-22 03:01:04 +02:00
// paths
getBasePath = () => {
return StorageModel.getPath(
2024-07-25 16:44:08 +02:00
"chat" /* Chat */,
2024-07-22 03:01:04 +02:00
filePaths.chat.files(this.chatModel.id)
);
};
getFileContainerPath = () => {
2024-07-23 18:23:26 +02:00
return [...this.getBasePath(), "data" /* Data */];
};
getModelContainerPath = (modelName) => {
2024-07-23 18:23:26 +02:00
return [...this.getBasePath(), "model" /* Model */, modelName];
2024-07-22 13:09:33 +02:00
};
2024-07-22 03:01:04 +02:00
getFilePath = (fileId) => {
return [...this.getFileContainerPath(), fileId];
2024-07-22 03:01:04 +02:00
};
2024-07-22 18:11:18 +02:00
getFileContentPath = (fileId, fileContentId) => {
const filePath = this.getFilePath(fileId);
2024-07-22 18:11:18 +02:00
return [...filePath, fileContentId];
2024-07-22 03:01:04 +02:00
};
2024-07-22 23:33:57 +02:00
// handlers
2024-07-22 18:11:18 +02:00
handleStringifiedFileContent = (stringifiedFileContent) => {
const fileContent = parseValidObject(
stringifiedFileContent,
FileContentReference
);
2024-07-22 18:11:18 +02:00
if (fileContent == null) return;
this.handleFileContent(fileContent);
};
2024-07-22 18:11:18 +02:00
handleFileContent = (fileContent) => {
const didStore = this.storeFileContent(fileContent);
if (didStore == false) return;
this.boardsAndTasksModel.handleFileContent(fileContent);
2024-07-21 23:37:54 +02:00
};
2024-07-22 11:49:05 +02:00
// methods
addFileContentAndSend = (fileContent) => {
2024-07-22 18:11:18 +02:00
this.handleFileContent(fileContent);
this.chatModel.sendMessage("", fileContent);
2024-07-22 11:49:05 +02:00
};
// storage
2024-07-22 18:11:18 +02:00
storeFileContent = (fileContent) => {
const fileContentPath = this.getFileContentPath(
fileContent.fileId,
fileContent.fileContentId
2024-07-21 23:37:54 +02:00
);
2024-07-22 18:11:18 +02:00
const existingFileContent = this.storageModel.read(fileContentPath);
if (existingFileContent != null) return false;
const stringifiedContent = stringify(fileContent);
this.storageModel.write(fileContentPath, stringifiedContent);
return true;
2024-07-21 23:37:54 +02:00
};
listFileIds = () => {
2024-07-22 03:01:04 +02:00
return this.storageModel.list(this.getBasePath());
2024-07-21 23:37:54 +02:00
};
2024-07-22 18:11:18 +02:00
listFileContentIds = (fileId) => {
2024-07-22 03:01:04 +02:00
const filePath = this.getFilePath(fileId);
2024-07-22 12:39:26 +02:00
return this.storageModel.list(filePath);
};
2024-07-22 18:11:18 +02:00
selectLatestFileContentId = (fileContentIds) => {
return fileContentIds[fileContentIds.length - 1];
2024-07-21 23:37:54 +02:00
};
2024-07-22 18:11:18 +02:00
getFileContent = (fileId, fileContentName, reference) => {
const filePath = this.getFileContentPath(fileId, fileContentName);
const fileContentOrNull = this.storageModel.readStringifiable(
filePath,
reference
);
2024-07-22 18:11:18 +02:00
return fileContentOrNull;
};
2024-07-22 18:11:18 +02:00
getLatestFileContent = (fileId, reference) => {
const fileContentsIds = this.listFileContentIds(fileId);
const latestFileContentId = this.selectLatestFileContentId(fileContentsIds);
if (latestFileContentId == void 0) return null;
const fileContent = this.getFileContent(
fileId,
2024-07-22 18:11:18 +02:00
latestFileContentId,
reference
);
2024-07-22 18:11:18 +02:00
return fileContent;
2024-07-21 23:37:54 +02:00
};
// init
2024-07-26 03:14:55 +02:00
constructor(storageModel2, settingsModel2, chatModel) {
this.chatModel = chatModel;
2024-07-26 03:14:55 +02:00
this.settingsModel = settingsModel2;
2024-07-21 23:37:54 +02:00
this.storageModel = storageModel2;
2024-07-26 03:14:55 +02:00
this.boardsAndTasksModel = new BoardsAndTasksModel(
this.storageModel,
this.settingsModel,
chatModel,
this
);
}
2024-07-21 23:37:54 +02:00
// utility
2024-07-22 18:11:18 +02:00
static generateFileContentId = (creationDate) => {
return creationDate + v4_default();
2024-07-21 23:37:54 +02:00
};
2024-07-22 18:11:18 +02:00
static createFileContent = (fileId, type) => {
const creationDate = createTimestamp();
2024-07-22 18:11:18 +02:00
const fileContentId = _FileModel.generateFileContentId(creationDate);
return {
dataVersion: DATA_VERSION,
fileId,
2024-07-22 18:11:18 +02:00
fileContentId,
creationDate,
2024-07-22 11:49:05 +02:00
type
};
};
};
2024-07-22 18:11:18 +02:00
var FileContentReference = {
2024-07-22 01:40:53 +02:00
dataVersion: DATA_VERSION,
fileId: "",
2024-07-22 18:11:18 +02:00
fileContentId: "",
2024-07-22 01:40:53 +02:00
creationDate: "",
type: ""
};
2024-07-21 03:17:40 +02:00
// src/Model/Utility/crypto.ts
var IV_SIZE = 12;
var ENCRYPTION_ALG = "AES-GCM";
async function encryptString(plaintext, passphrase) {
if (!window.crypto.subtle) return plaintext;
const iv = generateIV();
const key = await importKey(passphrase, "encrypt");
const encryptedArray = await encrypt(iv, key, plaintext);
const encryptionData = {
iv: uInt8ToArray(iv),
encryptedArray: uInt8ToArray(encryptedArray)
};
return btoa(JSON.stringify(encryptionData));
}
async function decryptString(cyphertext, passphrase) {
try {
const encrypionData = JSON.parse(atob(cyphertext));
const iv = arrayToUint8(encrypionData.iv);
const encryptedArray = arrayToUint8(encrypionData.encryptedArray);
const key = await importKey(passphrase, "decrypt");
return await decrypt(iv, key, encryptedArray);
} catch {
return cyphertext;
}
}
function encode(string) {
return new TextEncoder().encode(string);
}
function decode(array) {
return new TextDecoder("utf-8").decode(array);
}
async function encrypt(iv, key, message) {
const arrayBuffer = await window.crypto.subtle.encrypt(
{ name: ENCRYPTION_ALG, iv },
key,
encode(message)
);
return new Uint8Array(arrayBuffer);
}
async function decrypt(iv, key, cyphertext) {
const arrayBuffer = await crypto.subtle.decrypt(
{ name: ENCRYPTION_ALG, iv },
key,
cyphertext
);
return arrayBufferToString(arrayBuffer);
}
async function hash(encoded) {
return await crypto.subtle.digest("SHA-256", encoded);
}
function generateIV() {
return crypto.getRandomValues(new Uint8Array(IV_SIZE));
}
async function importKey(passphrase, purpose) {
return await crypto.subtle.importKey(
"raw",
await hash(encode(passphrase)),
{ name: ENCRYPTION_ALG },
false,
[purpose]
);
}
function arrayBufferToString(arrayBuffer) {
const uInt8Array = new Uint8Array(arrayBuffer);
return decode(uInt8Array);
}
function uInt8ToArray(uInt8Array) {
return Array.from(uInt8Array);
}
function arrayToUint8(array) {
return new Uint8Array(array);
}
2024-07-22 02:05:27 +02:00
// src/Model/Chat/chatModel.ts
2024-07-20 21:10:06 +02:00
var ChatModel = class _ChatModel {
2024-07-20 20:42:43 +02:00
connectionModel;
2024-07-20 05:10:50 +02:00
storageModel;
2024-07-20 20:42:43 +02:00
settingsModel;
2024-07-20 14:16:26 +02:00
chatListModel;
fileModel;
2024-07-20 20:42:43 +02:00
// data
id;
2024-07-20 05:10:50 +02:00
info;
2024-07-20 18:10:42 +02:00
color;
chatMessageHandlerManager = new HandlerManager();
2024-07-22 21:07:44 +02:00
get secondaryChannels() {
return this.info.secondaryChannels.sort(localeCompare);
}
2024-07-22 03:01:04 +02:00
// paths
getBasePath = () => {
2024-07-26 03:14:55 +02:00
return StorageModel.getPath(
"chat" /* Chat */,
filePaths.chat.chatBase(this.id)
);
2024-07-22 03:01:04 +02:00
};
getInfoPath = () => {
2024-07-26 03:14:55 +02:00
return StorageModel.getPath(
"chat" /* Chat */,
filePaths.chat.info(this.id)
);
2024-07-22 03:01:04 +02:00
};
getColorPath = () => {
2024-07-26 03:14:55 +02:00
return StorageModel.getPath(
"chat" /* Chat */,
filePaths.chat.color(this.id)
);
2024-07-22 03:01:04 +02:00
};
getMessageDirPath = () => {
2024-07-26 03:14:55 +02:00
return StorageModel.getPath(
"chat" /* Chat */,
filePaths.chat.messages(this.id)
);
2024-07-22 03:01:04 +02:00
};
getMessagePath = (id) => {
return [...this.getMessageDirPath(), id];
};
2024-07-22 23:33:57 +02:00
// handlers
handleMessage = (body) => {
const chatMessage = parseValidObject(
body,
ChatMessageReference
);
if (chatMessage == null) return;
chatMessage.status = "received" /* Received */;
this.addMessage(chatMessage);
};
handleMessageSent = (chatMessage) => {
chatMessage.status = "sent" /* Sent */;
this.addMessage(chatMessage);
};
2024-07-22 03:01:04 +02:00
// settings
2024-07-20 05:10:50 +02:00
setPrimaryChannel = (primaryChannel) => {
this.info.primaryChannel = primaryChannel;
this.storeInfo();
2024-07-21 01:12:35 +02:00
this.subscribe();
2024-07-20 05:10:50 +02:00
};
setSecondaryChannels = (secondaryChannels) => {
this.info.secondaryChannels = secondaryChannels;
this.storeInfo();
};
2024-07-20 19:18:19 +02:00
setEncryptionKey = (key) => {
this.info.encryptionKey = key;
this.storeInfo();
};
2024-07-20 18:10:42 +02:00
setColor = (color) => {
this.color = color;
this.storeColor();
};
2024-07-20 20:42:43 +02:00
// messaging
2024-07-22 23:33:57 +02:00
addMessage = async (chatMessage) => {
await this.decryptMessage(chatMessage);
if (chatMessage.body != "") {
const messagePath = this.getMessagePath(chatMessage.id);
this.storageModel.writeStringifiable(messagePath, chatMessage);
this.chatMessageHandlerManager.trigger(chatMessage);
2024-07-22 23:33:57 +02:00
}
this.fileModel.handleStringifiedFileContent(chatMessage.stringifiedFile);
};
2024-07-22 18:11:18 +02:00
sendMessage = async (body, fileContent) => {
2024-07-20 21:10:06 +02:00
const senderName = this.settingsModel.username;
if (senderName == "") return false;
const allChannels = [this.info.primaryChannel];
for (const secondaryChannel of this.info.secondaryChannels) {
allChannels.push(secondaryChannel);
}
const combinedChannel = allChannels.join("/");
const chatMessage = await _ChatModel.createChatMessage(
2024-07-20 21:10:06 +02:00
combinedChannel,
senderName,
this.info.encryptionKey,
body,
2024-07-22 18:11:18 +02:00
fileContent
2024-07-20 21:10:06 +02:00
);
this.addMessage(chatMessage);
2024-07-20 21:10:06 +02:00
this.connectionModel.sendMessageOrStore(chatMessage);
return true;
2024-07-20 20:42:43 +02:00
};
2024-07-21 03:17:40 +02:00
decryptMessage = async (chatMessage) => {
const decryptedBody = await decryptString(
chatMessage.body,
this.info.encryptionKey
);
const decryptedFile = await decryptString(
chatMessage.stringifiedFile ?? "",
this.info.encryptionKey
);
2024-07-21 03:17:40 +02:00
chatMessage.body = decryptedBody;
chatMessage.stringifiedFile = decryptedFile;
2024-07-21 03:17:40 +02:00
};
2024-07-21 01:12:35 +02:00
subscribe = () => {
this.connectionModel.addChannel(this.info.primaryChannel);
2024-07-20 05:10:50 +02:00
};
2024-07-22 03:01:04 +02:00
// storage
storeInfo = () => {
this.storageModel.writeStringifiable(this.getInfoPath(), this.info);
};
storeColor = () => {
this.storageModel.write(this.getColorPath(), this.color);
};
delete = () => {
this.chatListModel.untrackChat(this);
const dirPath = this.getBasePath();
this.storageModel.removeRecursively(dirPath);
};
// load
loadInfo = () => {
const info = this.storageModel.readStringifiable(
this.getInfoPath(),
ChatInfoReference
);
if (info != null) {
this.info = info;
} else {
this.info = _ChatModel.generateChatInfo("0");
}
};
loadColor = () => {
const path = this.getColorPath();
const color = this.storageModel.read(path);
if (!color) {
this.color = "standard" /* Standard */;
} else {
this.color = color;
}
};
get messages() {
const messageIds = this.storageModel.list(
this.getMessageDirPath()
);
if (!Array.isArray(messageIds)) return [];
const chatMessages = [];
for (const messageId of messageIds) {
const messagePath = this.getMessagePath(messageId);
const chatMessage = this.storageModel.readStringifiable(messagePath, ChatMessageReference);
chatMessages.push(chatMessage);
}
const sorted = chatMessages.sort(
(a, b) => a.dateSent.localeCompare(b.dateSent)
);
return sorted;
}
2024-07-20 05:10:50 +02:00
// init
2024-07-20 20:42:43 +02:00
constructor(storageModel2, connectionModel2, settingsModel2, chatListModel2, chatId) {
2024-07-20 05:10:50 +02:00
this.id = chatId;
2024-07-20 20:42:43 +02:00
this.connectionModel = connectionModel2;
this.settingsModel = settingsModel2;
2024-07-20 05:10:50 +02:00
this.storageModel = storageModel2;
2024-07-20 20:42:43 +02:00
this.chatListModel = chatListModel2;
2024-07-21 21:45:18 +02:00
this.loadInfo();
this.loadColor();
2024-07-21 01:12:35 +02:00
this.subscribe();
2024-07-26 03:14:55 +02:00
this.fileModel = new FileModel2(this.storageModel, this.settingsModel, this);
2024-07-20 05:10:50 +02:00
}
2024-07-20 21:10:06 +02:00
// utility
static generateChatInfo = (primaryChannel) => {
return {
dataVersion: DATA_VERSION,
2024-07-20 21:10:06 +02:00
primaryChannel,
secondaryChannels: [],
encryptionKey: "",
hasUnreadMessages: false
};
2024-07-20 05:10:50 +02:00
};
2024-07-22 18:11:18 +02:00
static createChatMessage = async (channel, sender, encryptionKey, body, fileContent) => {
const chatMessage = {
dataVersion: DATA_VERSION,
2024-07-20 21:10:06 +02:00
id: v4_default(),
channel,
sender,
body,
dateSent: createTimestamp(),
status: "outbox" /* Outbox */,
stringifiedFile: ""
2024-07-20 21:10:06 +02:00
};
2024-07-22 18:11:18 +02:00
if (fileContent != void 0) {
const stringifiedFile = stringify(fileContent);
chatMessage.stringifiedFile = stringifiedFile;
}
if (encryptionKey != "") {
chatMessage.body = await encryptString(chatMessage.body, encryptionKey);
chatMessage.stringifiedFile = await encryptString(
chatMessage.stringifiedFile,
encryptionKey
);
2024-07-21 01:12:35 +02:00
}
return chatMessage;
2024-07-21 01:12:35 +02:00
};
2024-07-20 21:10:06 +02:00
};
2024-07-22 01:40:53 +02:00
var ChatInfoReference = {
dataVersion: DATA_VERSION,
primaryChannel: "",
secondaryChannels: [""],
encryptionKey: "",
hasUnreadMessages: true
};
var ChatMessageReference = {
dataVersion: DATA_VERSION,
id: "",
channel: "",
sender: "",
body: "",
dateSent: "",
status: "",
stringifiedFile: ""
};
2024-07-20 05:10:50 +02:00
2024-07-22 02:05:27 +02:00
// src/Model/Chat/chatListModel.ts
2024-07-20 05:10:50 +02:00
var ChatListModel = class {
storageModel;
2024-07-20 20:42:43 +02:00
settingsModel;
connectionModel;
2024-07-20 05:10:50 +02:00
// data
2024-07-20 16:49:01 +02:00
chatModels = /* @__PURE__ */ new Set();
2024-07-21 01:12:35 +02:00
// handlers
messageHandler = (data) => {
const channel = data.messageChannel;
const body = data.messageBody;
if (channel == void 0) return;
if (body == void 0) return;
this.routeMessageToCorrectChatModel(
channel,
(chatModel) => chatModel.handleMessage(body)
);
};
messageSentHandler = (chatMessage) => {
const channel = chatMessage.channel;
this.routeMessageToCorrectChatModel(
channel,
(chatModel) => chatModel.handleMessageSent(chatMessage)
);
};
// methods
routeMessageToCorrectChatModel = (channel, fn) => {
const allChannels = channel.split("/");
2024-07-21 01:12:35 +02:00
for (const chatModel of this.chatModels) {
for (const channel2 of allChannels) {
if (channel2 != chatModel.info.primaryChannel) continue;
fn(chatModel);
2024-07-21 01:12:35 +02:00
break;
}
}
};
2024-07-22 03:01:04 +02:00
// storage
2024-07-20 05:10:50 +02:00
addChatModel = (chatModel) => {
2024-07-20 16:49:01 +02:00
this.chatModels.add(chatModel);
2024-07-20 05:10:50 +02:00
};
createChat = (primaryChannel) => {
const id = v4_default();
2024-07-20 20:42:43 +02:00
const chatModel = new ChatModel(
this.storageModel,
this.connectionModel,
this.settingsModel,
this,
id
);
2024-07-20 05:10:50 +02:00
chatModel.setPrimaryChannel(primaryChannel);
this.addChatModel(chatModel);
return chatModel;
};
untrackChat = (chat) => {
2024-07-20 16:49:01 +02:00
this.chatModels.delete(chat);
};
2024-07-21 21:45:18 +02:00
// load
2024-07-20 05:10:50 +02:00
loadChats = () => {
2024-07-25 16:44:08 +02:00
const chatDir = StorageModel.getPath("chat" /* Chat */, filePaths.chat.base);
2024-07-20 05:10:50 +02:00
const chatIds = this.storageModel.list(chatDir);
for (const chatId of chatIds) {
2024-07-20 20:42:43 +02:00
const chatModel = new ChatModel(
this.storageModel,
this.connectionModel,
this.settingsModel,
this,
chatId
);
2024-07-20 05:10:50 +02:00
this.addChatModel(chatModel);
}
};
// init
2024-07-20 20:42:43 +02:00
constructor(storageModel2, settingsModel2, connectionModel2) {
2024-07-20 05:10:50 +02:00
this.storageModel = storageModel2;
2024-07-20 20:42:43 +02:00
this.settingsModel = settingsModel2;
this.connectionModel = connectionModel2;
2024-07-20 05:10:50 +02:00
this.loadChats();
connectionModel2.messageHandlerManager.addHandler(this.messageHandler);
connectionModel2.messageSentHandlerManager.addHandler(
this.messageSentHandler
);
2024-07-20 05:10:50 +02:00
}
};
// src/View/utility.ts
function allowDrop(event) {
event.preventDefault();
}
function allowDrag(event) {
event.dataTransfer?.setData("text", "");
}
// src/ViewModel/Pages/taskViewModel.ts
var TaskViewModel = class {
// init
constructor(coreViewModel, boardsAndTasksModel, boardViewModel, calendarPageViewModel, taskFileContent) {
this.coreViewModel = coreViewModel;
this.boardsAndTasksModel = boardsAndTasksModel;
this.boardViewModel = boardViewModel;
this.calendarPageViewModel = calendarPageViewModel;
this.task = taskFileContent;
this.loadAllData();
this.selectedVersionId.subscribeSilent((selectedVersionId) => {
this.switchVersion(selectedVersionId);
});
}
boardsAndTasksModel;
boardViewModel;
calendarPageViewModel;
// data
task;
get sortingString() {
const splitDate = this.date.value.split("-");
const year = padZero(splitDate[0], 4);
const month = padZero(splitDate[1], 2);
const date = padZero(splitDate[2], 2);
const splitTime = this.time.value.split(":");
const hour = padZero(splitTime[0], 2);
const minute = padZero(splitTime[1], 2);
const priorityNumber = parseInt(this.priority.value);
const invertedPriority = 100 - priorityNumber;
return year + month + date + hour + minute + invertedPriority + this.name.value;
}
// paths
getFilePath = () => {
return this.boardsAndTasksModel.getTaskFilePath(this.task.fileId);
};
// state
index = new State(0);
boardId = new State("");
name = new State("");
description = new State("");
category = new State("");
status = new State("");
priority = new State("");
date = new State("");
time = new State("");
selectedVersionId = new State("");
versionIds = new ListState();
// methods
dragStart = (event) => {
allowDrag(event);
this.coreViewModel.draggedObject.value = this;
};
setCategoryAndStatus = (category, status) => {
if (category != void 0) this.category.value = category;
if (status != void 0) this.status.value = status;
this.save();
};
setBoardId = (boardId) => {
this.boardId.value = boardId;
this.save();
};
// view
open = () => {
this.boardViewModel?.selectTask(this);
};
close = () => {
this.boardViewModel?.closeTask();
};
closeAndSave = () => {
this.close();
this.save();
};
updateIndex = () => {
if (this.boardViewModel == null) return;
const index = this.boardViewModel.taskIndexManager.getIndex(this);
this.index.value = index;
};
// settings
save = () => {
const newTaskFileContent = BoardsAndTasksModel.createTaskFileContent(
this.task.fileId,
this.name.value,
this.task.boardId
);
newTaskFileContent.boardId = this.boardId.value;
newTaskFileContent.description = this.description.value;
newTaskFileContent.status = this.status.value;
newTaskFileContent.category = this.category.value;
newTaskFileContent.priority = this.priority.value;
newTaskFileContent.date = this.date.value;
newTaskFileContent.time = this.time.value;
this.boardsAndTasksModel.updateTaskAndSend(newTaskFileContent);
if (this.boardViewModel != null) {
this.boardViewModel.showTaskInList(newTaskFileContent);
this.boardViewModel.updateTaskIndices();
}
if (this.calendarPageViewModel != null) {
2024-07-26 03:52:05 +02:00
this.calendarPageViewModel.showTask(newTaskFileContent);
}
};
deleteTask = () => {
this.close();
this.boardsAndTasksModel.deleteTask(this.task.boardId, this.task.fileId);
if (this.boardViewModel != null) {
this.boardViewModel.removeTaskFromList(this.task.fileId);
}
if (this.calendarPageViewModel != null) {
2024-07-26 03:52:05 +02:00
this.calendarPageViewModel.removeTaskFromView(this.task);
}
};
// load
loadVersionIds = () => {
const versionIds = this.boardsAndTasksModel.listTaskVersionIds(
this.task.fileId
);
const sortedVersionIds = versionIds.sort(localeCompare).reverse();
this.versionIds.clear();
this.versionIds.add(...sortedVersionIds);
};
switchVersion = (versionId) => {
const taskFileContent = this.boardsAndTasksModel.getSpecificTaskFileContent(
this.task.fileId,
versionId
);
if (taskFileContent == null) return;
this.task = taskFileContent;
this.loadTaskData();
};
loadAllData = () => {
this.loadTaskData();
this.loadVersionIds();
};
loadTaskData = () => {
this.boardId.value = this.task.boardId;
this.name.value = this.task.name;
this.description.value = this.task.description ?? "";
this.category.value = this.task.category ?? "";
this.status.value = this.task.status ?? "";
this.priority.value = this.task.priority ?? "";
this.date.value = this.task.date ?? "";
this.time.value = this.task.time ?? "";
this.selectedVersionId.value = this.task.fileContentId;
};
2024-07-26 03:14:55 +02:00
// utility
static getStringsForFilter = (taskViewModel) => {
return [
taskViewModel.task.name,
taskViewModel.task.description ?? "",
taskViewModel.task.category ?? "",
taskViewModel.task.status ?? "",
taskViewModel.task.priority ?? "",
taskViewModel.task.date ?? "",
taskViewModel.task.time ?? ""
];
};
};
// src/ViewModel/Pages/calendarPageViewModel.ts
var CalendarPageViewModel = class {
// init
constructor(coreViewModel, storageModel2, calendarModel, boardAndTasksModel) {
this.coreViewModel = coreViewModel;
this.storageModel = storageModel2;
this.calendarModel = calendarModel;
2024-07-26 03:14:55 +02:00
this.boardsAndTasksModel = boardAndTasksModel;
bulkSubscribe([this.selectedYear, this.selectedMonth], () => {
this.loadTasks();
});
2024-07-26 03:52:05 +02:00
boardAndTasksModel.taskHandlerManager.addHandler(
(taskFileContent) => {
this.showTask(taskFileContent);
}
);
}
storageModel;
calendarModel;
2024-07-26 03:14:55 +02:00
boardsAndTasksModel;
// data
get monthString() {
return CalendarModel.getMonthString(
this.selectedYear.value.toString(),
this.selectedMonth.value.toString()
);
}
// paths
getBasePath = () => {
return [...this.calendarModel.getViewPath()];
};
// state
2024-07-26 03:14:55 +02:00
selectedYear = new State(0);
selectedMonth = new State(0);
2024-07-26 03:38:46 +02:00
selectedDate = new State(0);
2024-07-26 03:14:55 +02:00
monthGrid = new State(void 0);
// view
2024-07-26 03:14:55 +02:00
getTaskMapState = (taskFileContent) => {
if (this.monthGrid.value == null) return null;
const dateString = CalendarModel.isoToDateString(
taskFileContent.date ?? ""
);
if (dateString == void 0) return null;
return this.monthGrid.value.days[dateString];
};
showTask = (taskFileContent) => {
2024-07-26 03:52:05 +02:00
const monthString = CalendarModel.isoToMonthString(
taskFileContent.date ?? ""
);
if (monthString == void 0 || monthString != this.monthString) {
return;
}
const taskViewModel = new TaskViewModel(
this.coreViewModel,
2024-07-26 03:14:55 +02:00
this.boardsAndTasksModel,
null,
this,
taskFileContent
);
2024-07-26 03:14:55 +02:00
const mapState = this.getTaskMapState(taskFileContent);
mapState?.set(taskFileContent.fileId, taskViewModel);
};
2024-07-26 03:52:05 +02:00
removeTaskFromView = (taskFileContent) => {
2024-07-26 03:14:55 +02:00
const mapState = this.getTaskMapState(taskFileContent);
mapState?.remove(taskFileContent.fileId);
};
showToday = () => {
const today = /* @__PURE__ */ new Date();
this.selectedYear.value = today.getFullYear();
this.selectedMonth.value = today.getMonth() + 1;
2024-07-26 03:38:46 +02:00
this.selectedDate.value = today.getDate();
2024-07-26 03:14:55 +02:00
};
showPreviousMonth = () => {
this.selectedMonth.value -= 1;
if (this.selectedMonth.value <= 0) {
this.selectedYear.value -= 1;
this.selectedMonth.value = 12;
}
};
showNextMonth = () => {
this.selectedMonth.value += 1;
if (this.selectedMonth.value >= 13) {
this.selectedYear.value += 1;
this.selectedMonth.value = 1;
}
};
// storage
// load
2024-07-26 03:14:55 +02:00
loadTasks = () => {
this.monthGrid.value = this.calendarModel.generateMonthGrid(
this.selectedYear.value,
this.selectedMonth.value,
() => new MapState()
);
const taskIds = this.calendarModel.listTaskIds(this.monthString);
for (const taskId of taskIds) {
const taskFileContent = this.boardsAndTasksModel.getLatestTaskFileContent(taskId);
if (taskFileContent == null) continue;
this.showTask(taskFileContent);
}
};
loadData = () => {
2024-07-26 03:14:55 +02:00
this.loadTasks();
this.showToday();
};
};
2024-07-22 21:07:44 +02:00
// src/ViewModel/Chat/chatMessageViewModel.ts
2024-07-21 01:12:35 +02:00
var ChatMessageViewModel = class {
2024-07-25 16:20:43 +02:00
// init
constructor(coreViewModel, messagePageViewModel, chatMessage, sentByUser) {
this.coreViewModel = coreViewModel;
this.messagePageViewModel = messagePageViewModel;
this.chatMessage = chatMessage;
this.sentByUser = sentByUser;
this.loadData();
}
2024-07-22 21:07:44 +02:00
messagePageViewModel;
2024-07-21 01:12:35 +02:00
// data
2024-07-21 13:13:41 +02:00
chatMessage;
2024-07-21 01:12:35 +02:00
channel;
sender;
dateSent;
body = new State("");
2024-07-26 03:14:55 +02:00
status = new State(
void 0
);
2024-07-21 01:53:25 +02:00
sentByUser;
2024-07-23 13:09:46 +02:00
// state
isPresentingInfoModal = new State(false);
2024-07-21 13:13:41 +02:00
// methods
copyMessage = () => {
navigator.clipboard.writeText(this.body.value);
};
resendMessage = () => {
2024-07-22 21:07:44 +02:00
this.messagePageViewModel.sendMessageFromBody(this.body.value);
2024-07-21 13:13:41 +02:00
};
decryptMessage = () => {
2024-07-22 21:07:44 +02:00
this.messagePageViewModel.decryptMessage(this);
2024-07-21 13:13:41 +02:00
};
2024-07-23 13:09:46 +02:00
// view
showInfoModal = () => {
this.isPresentingInfoModal.value = true;
};
hideInfoModal = () => {
this.isPresentingInfoModal.value = false;
};
2024-07-21 13:13:41 +02:00
// load
loadData = () => {
this.channel = this.chatMessage.channel;
this.sender = this.chatMessage.sender;
this.dateSent = new Date(this.chatMessage.dateSent).toLocaleString();
this.body.value = this.chatMessage.body;
this.status.value = this.chatMessage.status;
2024-07-21 13:13:41 +02:00
};
2024-07-21 01:12:35 +02:00
};
2024-07-22 21:07:44 +02:00
// src/ViewModel/Pages/messagePageViewModel.ts
var MessagePageViewModel = class {
2024-07-25 16:20:43 +02:00
// init
constructor(coreViewModel, chatViewModel) {
this.coreViewModel = coreViewModel;
this.chatViewModel = chatViewModel;
this.cannotSendMessage = createProxyState(
[this.chatViewModel.settingsViewModel.username, this.composingMessage],
() => this.chatViewModel.settingsViewModel.username.value == "" || this.composingMessage.value == ""
);
}
2024-07-22 21:07:44 +02:00
chatViewModel;
// state
chatMessageViewModels = new MapState();
composingMessage = new State("");
// guards
cannotSendMessage;
// methods
sendMessage = () => {
if (this.cannotSendMessage.value == true) return;
this.sendMessageFromBody(this.composingMessage.value);
this.composingMessage.value = "";
};
sendMessageFromBody = (body) => {
this.chatViewModel.chatModel.sendMessage(body);
};
decryptMessage = async (messageViewModel) => {
const chatMessage = messageViewModel.chatMessage;
await this.chatViewModel.chatModel.decryptMessage(chatMessage);
this.chatViewModel.chatModel.addMessage(chatMessage);
messageViewModel.loadData();
};
// view
showChatMessage = (chatMessage) => {
const chatMessageModel = new ChatMessageViewModel(
2024-07-25 16:20:43 +02:00
this.coreViewModel,
this,
chatMessage,
chatMessage.sender == this.chatViewModel.settingsViewModel.username.value
);
const existingChatMessageViewModel = this.chatMessageViewModels.value.get(chatMessage.id);
if (existingChatMessageViewModel != void 0) {
existingChatMessageViewModel.body.value = chatMessage.body;
existingChatMessageViewModel.status.value = chatMessage.status;
} else {
this.chatMessageViewModels.set(chatMessage.id, chatMessageModel);
}
};
2024-07-22 21:07:44 +02:00
// load
loadData = () => {
for (const chatMessage of this.chatViewModel.chatModel.messages) {
this.showChatMessage(chatMessage);
2024-07-22 21:07:44 +02:00
}
};
};
// src/ViewModel/Pages/settingsPageViewModel.ts
var SettingsPageViewModel = class {
2024-07-25 16:20:43 +02:00
// init
constructor(coreViewModel, chatViewModel) {
this.coreViewModel = coreViewModel;
this.chatViewModel = chatViewModel;
this.loadListRelevantData();
this.cannotSetEncryptionKey = createProxyState(
[this.encryptionKeyInput],
() => this.encryptionKeyInput.value == this.chatViewModel.chatModel.info.encryptionKey
);
this.color.subscribe((newColor) => {
this.applyColor(newColor);
});
}
2024-07-22 21:07:44 +02:00
chatViewModel;
2024-07-20 14:16:26 +02:00
// state
primaryChannel = new State("");
primaryChannelInput = new State("");
2024-07-20 18:49:05 +02:00
secondaryChannels = new ListState();
newSecondaryChannelInput = new State("");
2024-07-20 19:18:19 +02:00
encryptionKeyInput = new State("");
shouldShowEncryptionKey = new State(false);
encryptionKeyInputType = createProxyState(
[this.shouldShowEncryptionKey],
() => this.shouldShowEncryptionKey.value == true ? "text" : "password"
);
2024-07-20 18:10:42 +02:00
color = new State("standard" /* Standard */);
2024-07-20 14:16:26 +02:00
// guards
cannotSetPrimaryChannel = createProxyState(
[this.primaryChannel, this.primaryChannelInput],
() => this.primaryChannelInput.value == "" || this.primaryChannelInput.value == this.primaryChannel.value
);
2024-07-20 23:22:08 +02:00
cannotAddSecondaryChannel = createProxyState(
[this.newSecondaryChannelInput],
() => this.newSecondaryChannelInput.value == ""
);
2024-07-20 19:18:19 +02:00
cannotSetEncryptionKey;
2024-07-22 21:07:44 +02:00
// methods
2024-07-20 17:30:36 +02:00
setPrimaryChannel = () => {
2024-07-22 21:07:44 +02:00
this.chatViewModel.chatModel.setPrimaryChannel(
this.primaryChannelInput.value
);
this.primaryChannel.value = this.chatViewModel.chatModel.info.primaryChannel;
2024-07-23 14:15:01 +02:00
this.chatViewModel.chatListViewModel.updateIndices();
2024-07-20 17:30:36 +02:00
};
2024-07-20 18:49:05 +02:00
addSecondaryChannel = () => {
this.secondaryChannels.add(this.newSecondaryChannelInput.value);
this.newSecondaryChannelInput.value = "";
this.storeSecondaryChannels();
2024-07-21 21:45:18 +02:00
this.loadSecondaryChannels();
2024-07-20 18:49:05 +02:00
};
removeSecondaryChannel = (secondaryChannel) => {
this.secondaryChannels.remove(secondaryChannel);
this.storeSecondaryChannels();
};
storeSecondaryChannels = () => {
2024-07-22 21:07:44 +02:00
this.chatViewModel.chatModel.setSecondaryChannels([
2024-07-20 18:49:05 +02:00
...this.secondaryChannels.value.values()
]);
};
2024-07-20 19:18:19 +02:00
setEncryptionKey = () => {
2024-07-22 21:07:44 +02:00
this.chatViewModel.chatModel.setEncryptionKey(
this.encryptionKeyInput.value
);
2024-07-20 19:18:19 +02:00
this.encryptionKeyInput.callSubscriptions();
};
2024-07-23 13:48:17 +02:00
applyColor = (newColor) => {
this.chatViewModel.setColor(newColor);
2024-07-20 18:10:42 +02:00
};
2024-07-20 20:48:13 +02:00
remove = () => {
2024-07-22 21:07:44 +02:00
this.chatViewModel.close();
this.chatViewModel.chatModel.delete();
2024-07-23 01:22:08 +02:00
this.chatViewModel.chatListViewModel.untrackChat(this.chatViewModel);
2024-07-20 20:48:13 +02:00
};
2024-07-22 21:07:44 +02:00
// load
2024-07-22 23:33:57 +02:00
loadListRelevantData = () => {
2024-07-22 21:07:44 +02:00
this.primaryChannel.value = this.chatViewModel.chatModel.info.primaryChannel;
this.color.value = this.chatViewModel.chatModel.color;
2024-07-20 21:10:06 +02:00
};
2024-07-22 21:07:44 +02:00
loadData = () => {
this.primaryChannelInput.value = this.chatViewModel.chatModel.info.primaryChannel;
this.loadSecondaryChannels();
this.encryptionKeyInput.value = this.chatViewModel.chatModel.info.encryptionKey;
2024-07-21 13:13:41 +02:00
};
2024-07-22 21:07:44 +02:00
loadSecondaryChannels = () => {
this.secondaryChannels.clear();
for (const secondaryChannel of this.chatViewModel.chatModel.secondaryChannels) {
this.secondaryChannels.add(secondaryChannel);
}
};
};
2024-07-23 00:57:40 +02:00
// src/ViewModel/Pages/boardViewModel.ts
var BoardViewModel = class {
2024-07-25 16:20:43 +02:00
// init
constructor(coreViewModel, storageModel2, boardModel, taskPageViewModel, boardInfo) {
this.coreViewModel = coreViewModel;
this.storageModel = storageModel2;
this.boardsAndTasksModel = boardModel;
2024-07-25 16:20:43 +02:00
this.taskPageViewModel = taskPageViewModel;
this.boardInfo = boardInfo;
this.loadListRelevantData();
this.isSelected = createProxyState(
[this.taskPageViewModel.selectedBoardId],
() => this.taskPageViewModel.selectedBoardId.value == this.boardInfo.fileId
);
this.color.subscribe(() => {
if (this.isSelected.value == false) return;
this.applyColor();
});
this.selectedPage.subscribeSilent(() => {
this.storeLastUsedView();
});
boardModel.taskHandlerManager.addHandler(
(taskFileContent) => {
if (taskFileContent.boardId != this.boardInfo.fileId) return;
this.showTaskInList(taskFileContent);
this.updateTaskIndices();
}
);
}
storageModel;
boardsAndTasksModel;
2024-07-23 00:57:40 +02:00
taskPageViewModel;
2024-07-24 01:19:44 +02:00
// data
2024-07-23 00:57:40 +02:00
boardInfo;
2024-07-24 14:34:28 +02:00
taskIndexManager = new IndexManager(
(taskViewModel) => taskViewModel.sortingString
);
2024-07-24 01:19:44 +02:00
// state
2024-07-23 00:57:40 +02:00
name = new State("");
color = new State("standard" /* Standard */);
2024-07-24 13:31:28 +02:00
index = new State(0);
2024-07-23 15:41:03 +02:00
selectedPage = new State(
"list" /* List */
);
2024-07-24 13:31:28 +02:00
selectedTaskViewModel = new State(void 0);
isSelected;
isPresentingSettingsModal = new State(false);
isPresentingFilterModal = new State(false);
2024-07-25 15:31:27 +02:00
taskViewModels = new MapState();
filteredTaskViewModels = new ListState();
// paths
getBasePath = () => {
return [...this.taskPageViewModel.getBoardViewPath(this.boardInfo.fileId)];
};
getLastUsedBoardPath = () => {
return [...this.getBasePath(), "last-used-view" /* LastUsedView */];
};
2024-07-24 01:19:44 +02:00
// settings
saveSettings = () => {
const newBoardInfoFileContent = BoardsAndTasksModel.createBoardInfoFileContent(
this.boardInfo.fileId,
this.name.value,
this.color.value
);
this.taskPageViewModel.updateBoard(newBoardInfoFileContent);
};
2024-07-23 13:53:50 +02:00
applyColor = () => {
this.taskPageViewModel.chatViewModel.setDisplayedColor(this.color.value);
2024-07-23 13:48:17 +02:00
};
2024-07-23 14:31:50 +02:00
deleteBoard = () => {
this.taskPageViewModel.deleteBoard(this.boardInfo);
this.close();
};
2024-07-24 14:34:28 +02:00
// methods
2024-07-24 01:19:44 +02:00
createTask = () => {
const taskFileContent = this.boardsAndTasksModel.createTask(this.boardInfo.fileId);
2024-07-24 13:31:28 +02:00
const taskViewModel = new TaskViewModel(
2024-07-25 16:20:43 +02:00
this.coreViewModel,
this.boardsAndTasksModel,
2024-07-24 13:31:28 +02:00
this,
null,
2024-07-24 13:31:28 +02:00
taskFileContent
);
this.selectTask(taskViewModel);
2024-07-24 14:34:28 +02:00
this.updateTaskIndices();
};
removeTaskFromList = (taskId) => {
this.taskViewModels.remove(taskId);
this.updateIndex();
2024-07-24 01:19:44 +02:00
};
handleDropWithinBoard = (category, status) => {
2024-07-25 16:20:43 +02:00
const draggedObject = this.coreViewModel.draggedObject.value;
if (draggedObject instanceof TaskViewModel == false) return;
draggedObject.setCategoryAndStatus(category, status);
};
handleDropBetweenBoards = () => {
const draggedObject = this.coreViewModel.draggedObject.value;
if (draggedObject instanceof TaskViewModel == false) return;
draggedObject.setBoardId(this.boardInfo.fileId);
};
2024-07-24 01:19:44 +02:00
// storage
storeLastUsedView = () => {
const path = this.getLastUsedBoardPath();
const lastUsedView = this.selectedPage.value;
this.storageModel.write(path, lastUsedView);
};
restoreLastUsedView = () => {
const path = this.getLastUsedBoardPath();
const lastUsedView = this.storageModel.read(path);
if (lastUsedView == null) return;
this.selectedPage.value = lastUsedView;
};
2024-07-24 13:31:28 +02:00
// view
2024-07-24 13:40:17 +02:00
showTaskInList = (taskFileContent) => {
if (taskFileContent.boardId != this.boardInfo.fileId) {
this.boardsAndTasksModel.deleteTaskReference(
this.boardInfo.fileId,
taskFileContent.fileId
);
this.removeTaskFromList(taskFileContent.fileId);
return;
}
2024-07-24 13:40:17 +02:00
const taskViewModel = new TaskViewModel(
2024-07-25 16:20:43 +02:00
this.coreViewModel,
this.boardsAndTasksModel,
2024-07-24 13:40:17 +02:00
this,
null,
2024-07-24 13:40:17 +02:00
taskFileContent
);
this.taskViewModels.set(taskFileContent.fileId, taskViewModel);
};
2024-07-24 13:31:28 +02:00
select = () => {
this.taskPageViewModel.selectBoard(this);
};
close = () => {
this.taskPageViewModel.closeBoard();
2024-07-25 03:00:10 +02:00
this.taskViewModels.clear();
2024-07-24 13:31:28 +02:00
};
showSettings = () => {
this.isPresentingSettingsModal.value = true;
};
hideSettings = () => {
this.saveSettings();
this.isPresentingSettingsModal.value = false;
};
showFilterModal = () => {
this.isPresentingFilterModal.value = true;
};
hideFilterModal = () => {
this.isPresentingFilterModal.value = false;
};
selectTask = (selectedTask) => {
this.selectedTaskViewModel.value = selectedTask;
};
closeTask = () => {
this.selectedTaskViewModel.value = void 0;
};
updateIndex = () => {
const index = this.taskPageViewModel.boardIndexManager.getIndex(this);
this.index.value = index;
};
2024-07-24 14:34:28 +02:00
updateTaskIndices = () => {
this.taskIndexManager.update([...this.taskViewModels.value.values()]);
for (const boardViewModel of this.taskViewModels.value.values()) {
boardViewModel.updateIndex();
}
};
2024-07-23 00:57:40 +02:00
// load
loadListRelevantData = () => {
this.name.value = this.boardInfo.name;
this.color.value = this.boardInfo.color;
};
2024-07-24 01:19:44 +02:00
loadTasks = () => {
const taskIds = this.boardsAndTasksModel.listTaskIds(
2024-07-24 01:19:44 +02:00
this.boardInfo.fileId
);
for (const taskId of taskIds) {
2024-07-25 03:00:10 +02:00
if (this.taskViewModels.value.has(taskId)) return;
const taskFileContent = this.boardsAndTasksModel.getLatestTaskFileContent(taskId);
2024-07-24 01:19:44 +02:00
if (taskFileContent == null) continue;
const taskViewModel = new TaskViewModel(
2024-07-25 16:20:43 +02:00
this.coreViewModel,
this.boardsAndTasksModel,
2024-07-24 01:19:44 +02:00
this,
null,
2024-07-24 01:19:44 +02:00
taskFileContent
);
this.taskViewModels.set(taskFileContent.fileId, taskViewModel);
}
2024-07-24 14:34:28 +02:00
this.updateTaskIndices();
2024-07-24 01:19:44 +02:00
};
2024-07-23 00:57:40 +02:00
loadData = () => {
this.restoreLastUsedView();
2024-07-24 01:19:44 +02:00
this.loadTasks();
2024-07-23 00:57:40 +02:00
};
2024-07-25 16:20:43 +02:00
};
// src/ViewModel/Pages/taskPageViewModel.ts
var TaskPageViewModel = class {
2024-07-23 00:57:40 +02:00
// init
2024-07-25 16:20:43 +02:00
constructor(coreViewModel, storageModel2, boardModel, chatViewModel) {
this.coreViewModel = coreViewModel;
this.storageModel = storageModel2;
this.boardsAndTasksModel = boardModel;
2024-07-25 16:20:43 +02:00
this.chatViewModel = chatViewModel;
boardModel.boardHandlerManager.addHandler(
(boardInfoFileContent) => {
this.showBoardInList(boardInfoFileContent);
this.updateBoardIndices();
2024-07-24 13:40:17 +02:00
}
);
2024-07-23 00:57:40 +02:00
}
2024-07-23 02:50:22 +02:00
storageModel;
boardsAndTasksModel;
2024-07-23 02:50:22 +02:00
chatViewModel;
2024-07-23 14:27:38 +02:00
// data
boardIndexManager = new IndexManager(
(boardViewModel) => boardViewModel.name.value
);
2024-07-23 18:51:41 +02:00
// paths
getBasePath = () => {
return [...this.boardsAndTasksModel.getViewPath()];
};
getBoardViewPath = (boardId) => {
return [...this.getBasePath(), boardId];
2024-07-23 18:51:41 +02:00
};
getLastUsedBoardPath = () => {
2024-07-23 21:19:56 +02:00
return [...this.getBasePath(), "last-used-board" /* LastUsedBoard */];
2024-07-23 18:51:41 +02:00
};
2024-07-22 21:07:44 +02:00
// state
newBoardNameInput = new State("");
2024-07-23 01:40:30 +02:00
boardViewModels = new MapState();
2024-07-26 03:14:55 +02:00
selectedBoardId = new State(
void 0
);
2024-07-22 21:07:44 +02:00
// guards
cannotCreateBoard = createProxyState(
[this.newBoardNameInput],
() => this.newBoardNameInput.value == ""
);
// methods
createBoard = () => {
if (this.cannotCreateBoard.value == true) return;
const boardInfoFileContent = this.boardsAndTasksModel.createBoard(this.newBoardNameInput.value);
2024-07-22 21:07:44 +02:00
this.newBoardNameInput.value = "";
2024-07-23 02:40:11 +02:00
this.showBoardInList(boardInfoFileContent);
this.boardsAndTasksModel.updateBoardAndSend(boardInfoFileContent);
2024-07-24 14:34:28 +02:00
this.updateBoardIndices();
2024-07-23 00:05:48 +02:00
};
updateBoard = (boardInfoFileContent) => {
this.boardsAndTasksModel.updateBoardAndSend(boardInfoFileContent);
2024-07-24 14:34:28 +02:00
this.updateBoardIndices();
};
2024-07-23 14:31:50 +02:00
deleteBoard = (boardInfoFileContent) => {
this.boardsAndTasksModel.deleteBoard(boardInfoFileContent.fileId);
2024-07-23 14:31:50 +02:00
this.boardViewModels.remove(boardInfoFileContent.fileId);
2024-07-24 14:34:28 +02:00
this.updateBoardIndices();
2024-07-23 14:31:50 +02:00
};
// view
2024-07-23 02:40:11 +02:00
showBoardInList = (boardInfo) => {
const boardViewModel = new BoardViewModel(
2024-07-25 16:20:43 +02:00
this.coreViewModel,
2024-07-24 01:19:44 +02:00
this.storageModel,
this.boardsAndTasksModel,
this,
2024-07-24 01:19:44 +02:00
boardInfo
);
2024-07-23 01:40:30 +02:00
this.boardViewModels.set(boardInfo.fileId, boardViewModel);
};
2024-07-23 02:40:11 +02:00
selectBoard = (boardViewModel) => {
2024-07-23 13:51:27 +02:00
this.selectedBoardId.value = boardViewModel.boardInfo.fileId;
2024-07-23 02:50:22 +02:00
this.chatViewModel.displayedColor.value = boardViewModel.color.value;
2024-07-23 18:51:41 +02:00
this.storeLastUsedBoard();
2024-07-23 02:40:11 +02:00
};
closeBoard = () => {
2024-07-23 13:51:27 +02:00
this.selectedBoardId.value = void 0;
2024-07-23 02:50:22 +02:00
this.chatViewModel.resetColor();
2024-07-23 18:51:41 +02:00
this.storeLastUsedBoard();
2024-07-23 02:40:11 +02:00
};
2024-07-24 14:34:28 +02:00
updateBoardIndices = () => {
2024-07-23 14:27:38 +02:00
this.boardIndexManager.update([...this.boardViewModels.value.values()]);
for (const boardViewModel of this.boardViewModels.value.values()) {
boardViewModel.updateIndex();
}
};
2024-07-23 18:51:41 +02:00
// storage
storeLastUsedBoard = () => {
const path = this.getLastUsedBoardPath();
const lastUsedBoardId = this.selectedBoardId.value ?? "";
this.storageModel.write(path, lastUsedBoardId);
};
openLastUsedBoard = () => {
const path = this.getLastUsedBoardPath();
const lastUsedBoardId = this.storageModel.read(path);
if (lastUsedBoardId == null) return;
const boardViewModel = this.boardViewModels.value.get(lastUsedBoardId);
if (boardViewModel == void 0) return;
this.selectBoard(boardViewModel);
};
2024-07-23 00:05:48 +02:00
// load
loadData = () => {
2024-07-23 00:57:40 +02:00
this.boardViewModels.clear();
const boardIds = this.boardsAndTasksModel.listBoardIds();
2024-07-23 00:05:48 +02:00
for (const boardId of boardIds) {
const boardInfo = this.boardsAndTasksModel.getBoardInfo(boardId);
2024-07-23 00:05:48 +02:00
if (boardInfo == null) continue;
2024-07-23 02:40:11 +02:00
this.showBoardInList(boardInfo);
2024-07-23 00:05:48 +02:00
}
2024-07-24 14:34:28 +02:00
this.updateBoardIndices();
2024-07-23 18:51:41 +02:00
this.openLastUsedBoard();
2024-07-22 21:07:44 +02:00
};
2024-07-25 16:20:43 +02:00
};
// src/ViewModel/Chat/chatViewModel.ts
var ChatViewModel = class {
2024-07-22 21:07:44 +02:00
// init
2024-07-25 16:20:43 +02:00
constructor(coreViewModel, storageModel2, chatModel, settingsViewModel2, chatListViewModel2) {
this.coreViewModel = coreViewModel;
2024-07-23 02:50:22 +02:00
this.storageModel = storageModel2;
2024-07-25 16:20:43 +02:00
this.chatModel = chatModel;
this.settingsViewModel = settingsViewModel2;
this.chatListViewModel = chatListViewModel2;
this.calendarViewModel = new CalendarPageViewModel(
coreViewModel,
this.storageModel,
2024-07-26 03:14:55 +02:00
this.chatModel.fileModel.boardsAndTasksModel.calendarModel,
this.chatModel.fileModel.boardsAndTasksModel
);
2024-07-25 16:20:43 +02:00
this.taskPageViewModel = new TaskPageViewModel(
this.coreViewModel,
this.storageModel,
this.chatModel.fileModel.boardsAndTasksModel,
this
);
this.messagePageViewModel = new MessagePageViewModel(
this.coreViewModel,
this
);
this.settingsPageViewModel = new SettingsPageViewModel(
this.coreViewModel,
this
);
chatModel.chatMessageHandlerManager.addHandler(
(chatMessage) => {
this.messagePageViewModel.showChatMessage(chatMessage);
}
);
2024-07-25 16:20:43 +02:00
this.loadPageSelection();
this.resetColor();
2024-07-22 21:07:44 +02:00
}
chatModel;
storageModel;
settingsViewModel;
chatListViewModel;
calendarViewModel;
2024-07-22 21:07:44 +02:00
taskPageViewModel;
messagePageViewModel;
settingsPageViewModel;
// state
2024-07-23 02:50:22 +02:00
displayedColor = new State("standard" /* Standard */);
2024-07-22 21:07:44 +02:00
selectedPage = new State(
"messages" /* Messages */
);
2024-07-23 02:50:22 +02:00
index = new State(0);
2024-07-22 21:07:44 +02:00
// view
open = () => {
this.chatListViewModel.openChat(this);
};
close = () => {
this.chatListViewModel.closeChat();
2024-07-21 13:13:41 +02:00
};
2024-07-23 02:50:22 +02:00
closeSubPages = () => {
};
setColor = (color) => {
2024-07-23 13:48:17 +02:00
this.setDisplayedColor(color);
this.chatModel.setColor(color);
};
2024-07-23 13:48:17 +02:00
setDisplayedColor = (color) => {
this.displayedColor.value = color;
};
2024-07-23 02:50:22 +02:00
resetColor = () => {
this.displayedColor.value = this.settingsPageViewModel.color.value;
};
2024-07-23 14:15:01 +02:00
updateIndex = () => {
2024-07-23 14:27:38 +02:00
const index = this.chatListViewModel.chatIndexManager.getIndex(this);
2024-07-23 14:15:01 +02:00
this.index.value = index;
};
2024-07-21 21:45:18 +02:00
// load
loadPageSelection = () => {
2024-07-22 03:02:33 +02:00
const path = StorageModel.getPath(
2024-07-25 16:44:08 +02:00
"chat" /* Chat */,
2024-07-22 03:02:33 +02:00
filePaths.chat.lastUsedPage(this.chatModel.id)
);
2024-07-21 21:45:18 +02:00
const lastUsedPage = this.storageModel.read(path);
2024-07-20 16:34:16 +02:00
if (lastUsedPage != null) {
this.selectedPage.value = lastUsedPage;
}
this.selectedPage.subscribeSilent((newPage) => {
2024-07-21 21:45:18 +02:00
this.storageModel.write(path, newPage);
2024-07-23 18:51:41 +02:00
this.resetColor();
2024-07-20 16:34:16 +02:00
});
};
2024-07-20 14:16:26 +02:00
};
2024-07-22 21:07:44 +02:00
// src/ViewModel/Chat/chatListViewModel.ts
2024-07-20 05:10:50 +02:00
var ChatListViewModel = class {
2024-07-25 16:20:43 +02:00
// init
constructor(coreViewModel, storageModel2, chatListModel2, settingsViewModel2) {
this.coreViewModel = coreViewModel;
this.storageModel = storageModel2;
this.chatListModel = chatListModel2;
this.settingsViewModel = settingsViewModel2;
this.loadChats();
}
2024-07-20 20:42:43 +02:00
storageModel;
2024-07-24 01:19:44 +02:00
chatListModel;
2024-07-20 21:10:06 +02:00
settingsViewModel;
2024-07-23 14:27:38 +02:00
// data
chatIndexManager = new IndexManager(
(chatViewModel) => chatViewModel.settingsPageViewModel.primaryChannel.value
);
2024-07-20 05:10:50 +02:00
// state
newChatPrimaryChannel = new State("");
2024-07-20 14:16:26 +02:00
chatViewModels = new ListState();
2024-07-26 03:14:55 +02:00
selectedChat = new State(
void 0
);
2024-07-20 05:10:50 +02:00
// guards
cannotCreateChat = createProxyState(
[this.newChatPrimaryChannel],
() => this.newChatPrimaryChannel.value == ""
);
// methods
createChat = () => {
const chatModel = this.chatListModel.createChat(
this.newChatPrimaryChannel.value
);
this.newChatPrimaryChannel.value = "";
2024-07-20 20:42:43 +02:00
const chatViewModel = this.createChatViewModel(chatModel);
2024-07-23 14:15:01 +02:00
this.trackChat(chatViewModel);
this.updateIndices();
};
trackChat = (chatViewModel) => {
2024-07-20 14:16:26 +02:00
this.chatViewModels.add(chatViewModel);
2024-07-20 05:10:50 +02:00
};
2024-07-20 14:16:26 +02:00
untrackChat = (chatViewModel) => {
this.chatListModel.untrackChat(chatViewModel.chatModel);
this.chatViewModels.remove(chatViewModel);
};
2024-07-20 20:42:43 +02:00
createChatViewModel = (chatModel) => {
2024-07-20 21:10:06 +02:00
return new ChatViewModel(
2024-07-25 16:20:43 +02:00
this.coreViewModel,
2024-07-20 21:10:06 +02:00
this.storageModel,
2024-07-24 01:19:44 +02:00
chatModel,
2024-07-20 21:10:06 +02:00
this.settingsViewModel,
this
);
2024-07-20 20:42:43 +02:00
};
2024-07-23 14:15:01 +02:00
updateIndices = () => {
2024-07-23 14:27:38 +02:00
this.chatIndexManager.update([...this.chatViewModels.value.values()]);
2024-07-23 14:15:01 +02:00
for (const chatViewModel of this.chatViewModels.value) {
chatViewModel.updateIndex();
}
};
2024-07-20 20:42:43 +02:00
// view
2024-07-20 14:16:26 +02:00
openChat = (chatViewModel) => {
this.selectedChat.value = chatViewModel;
};
closeChat = () => {
this.selectedChat.value = void 0;
2024-07-20 05:10:50 +02:00
};
2024-07-21 21:45:18 +02:00
// load
loadChats = () => {
2024-07-20 20:48:13 +02:00
this.chatViewModels.clear();
2024-07-20 14:16:26 +02:00
for (const chatModel of this.chatListModel.chatModels.values()) {
2024-07-20 20:42:43 +02:00
const chatViewModel = this.createChatViewModel(chatModel);
2024-07-23 14:15:01 +02:00
this.trackChat(chatViewModel);
2024-07-20 05:10:50 +02:00
}
2024-07-23 14:15:01 +02:00
this.updateIndices();
2024-07-20 05:10:50 +02:00
};
};
2024-07-20 02:30:45 +02:00
// src/View/translations.ts
var englishTranslations = {
general: {
2024-07-20 17:30:36 +02:00
deleteItemButtonAudioLabel: "delete item",
2024-07-25 15:31:27 +02:00
searchButtonAudioLabel: "search",
abortButton: "Abort",
2024-07-24 13:31:28 +02:00
closeButton: "Close",
confirmButton: "Confirm",
2024-07-24 13:31:28 +02:00
saveButton: "Save",
2024-07-24 15:57:02 +02:00
setButton: "Set",
2024-07-25 15:31:27 +02:00
fileVersionLabel: "Version",
searchLabel: "Search"
2024-07-20 02:30:45 +02:00
},
regional: {
weekdays: {
full: [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
],
2024-07-20 16:28:00 +02:00
abbreviated: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
2024-07-20 02:30:45 +02:00
}
},
homePage: {
appName: "Comms",
///
overviewHeadline: "Overview",
serverAddress: "Server address",
serverAddressPlaceholder: "wss://192.168.0.69:3000",
connectAudioLabel: "connect to server",
disconnectAudioLabel: "disconnect from server",
manageConnectionsAudioLabel: "manage connections",
yourNameLabel: "Your name",
yourNamePlaceholder: "Jane Doe",
2024-07-20 17:30:36 +02:00
setNameButtonAudioLabel: "set name",
2024-07-20 02:30:45 +02:00
firstDayOfWeekLabel: "First day of week",
2024-07-21 17:55:53 +02:00
manageStorageButton: "Manage storage",
2024-07-20 02:30:45 +02:00
scrollToChatButton: "Chats",
///
backToOverviewAudioLabel: "go back to overview",
chatsHeadline: "Chats",
addChatAudioLabel: "name of new chat",
addChatPlaceholder: "Add chat",
addChatButton: "Add chat"
},
connectionModal: {
connectionModalHeadline: "Manage Connections",
2024-07-20 02:30:45 +02:00
///
connectButtonAudioLabel: "connect"
2024-07-20 14:16:26 +02:00
},
storage: {
2024-07-21 19:44:10 +02:00
noItemSelected: "No item selected",
notAFile: "(not a file)",
contentEmpty: "(empty)",
path: "Path",
content: "Content",
deleteItem: "Delete item"
2024-07-21 19:44:10 +02:00
},
chatPage: {
closeChatAudioLabe: "close chat",
2024-07-20 16:28:00 +02:00
chatSettingsAudioLabel: "chat settings",
pages: {
settings: "Settings",
messages: "Messages",
2024-07-22 13:17:21 +02:00
tasks: "Tasks",
calendar: "Calendar"
2024-07-20 17:19:10 +02:00
},
settings: {
2024-07-20 17:30:36 +02:00
settingsHeadline: "Settings",
primaryChannelLabel: "Primary channel",
2024-07-20 18:49:05 +02:00
setPrimaryChannelButtonAudioLabel: "set primary channel",
newSecondaryChannelPlaceholder: "Add secondary channel",
newSecondaryChannelAudioLabel: "name of new secondary channel",
2024-07-20 19:18:19 +02:00
addSecondaryChannelButtonAudioLabel: "add secondary channel",
encryptionKeyLabel: "Encryption key",
setEncryptionKeyButtonAudioLabel: "set encryption key",
2024-07-20 20:48:13 +02:00
showEncryptionKey: "Show encryption key",
deleteChatButton: "Delete entire chat"
},
message: {
2024-07-22 21:07:44 +02:00
messagesHeadline: "Messages",
2024-07-23 13:09:46 +02:00
///
composerInputPlaceholder: "Type a message...",
2024-07-21 13:13:41 +02:00
sendMessageButtonAudioLabel: "send message",
2024-07-23 13:09:46 +02:00
///
2024-07-21 13:13:41 +02:00
showMessageInfoButtonAudioLabel: "show message info",
messageInfoHeadline: "Message Info",
sentBy: "Sent by",
timeSent: "Time sent",
channel: "Channel",
messageContent: "Message content",
copyMessageButton: "Copy message",
resendMessageButton: "Resend message",
decryptMessageButton: "Decrypt message",
deleteMessageButton: "Delete message"
2024-07-22 19:50:57 +02:00
},
task: {
newBoardNamePlaceholder: "Create a board",
2024-07-23 02:57:20 +02:00
createBoardButtonAudioLabel: "create board",
2024-07-23 13:09:46 +02:00
///
noBoardSelected: "No board selected",
2024-07-23 13:51:27 +02:00
boardNotFound: "Board not found",
2024-07-23 13:09:46 +02:00
///
closeBoard: "close board",
showBoardSettingsButtonAudioLabel: "show board settigns",
2024-07-23 15:25:16 +02:00
listViewButtonAudioLabel: "list view",
kanbanViewButtonAudioLabel: "kanban view",
statusViewButtonAudioLabel: "status grid view",
filterTasksButtonAudioLabel: "filter tasks",
createTaskButtonAudioLabel: "create new task",
2024-07-23 13:09:46 +02:00
///
boardSettingsHeadline: "Board Settings",
2024-07-23 14:31:50 +02:00
boardNameInputLabel: "Board name",
2024-07-24 13:31:28 +02:00
deleteBoardButton: "Delete board and all tasks",
///
2024-07-24 13:59:42 +02:00
taskSettingsHeadline: "Edit Task",
2024-07-24 13:31:28 +02:00
taskNameLabel: "Title",
taskCategoryLabel: "Category",
taskStatusLabel: "Status",
taskPriorityLabel: "Priority",
taskDescriptionLabel: "Description",
taskDateLabel: "Date",
2024-07-24 14:10:09 +02:00
taskTimeLabel: "Time",
2024-07-25 02:49:23 +02:00
deleteTaskButton: "Delete task",
///
2024-07-25 15:31:27 +02:00
filterTasksHeadline: "Filter Tasks",
///
2024-07-25 02:49:23 +02:00
renameCategoryInputPlaceholder: "Rename category,"
2024-07-26 03:14:55 +02:00
},
calendar: {
todayButtonAudioLabel: "go to today",
previousMonthButtonAudioLabel: "previous month",
nextMonthButtonAudioLabel: "next month",
yearInputAudioLabel: "year",
monthInputAudioLabel: "month",
yearInputPlaceholder: "2000",
2024-07-26 03:38:46 +02:00
monthInputPlaceholder: "01",
///
searchEventsHeadline: "Search Events"
2024-07-20 16:28:00 +02:00
}
}
2024-07-20 02:30:45 +02:00
};
var allTranslations = {
en: englishTranslations
};
var language = navigator.language.substring(0, 2);
var translations = allTranslations[language] || allTranslations.en;
2024-07-26 03:29:07 +02:00
// src/View/Components/monthGrid.tsx
2024-07-26 03:38:46 +02:00
function MonthGrid2(monthGrid, selectedDate) {
2024-07-26 03:29:07 +02:00
const dayLabels = [];
let currentWeekday = monthGrid.firstDayOfWeek;
while (dayLabels.length < 7) {
dayLabels.push(
/* @__PURE__ */ createElement("span", { class: "secondary" }, translations.regional.weekdays.abbreviated[currentWeekday])
);
currentWeekday++;
if (currentWeekday == 7) currentWeekday = 0;
}
const offsetElements = [];
for (let i = 0; i < monthGrid.offset; i++) {
offsetElements.push(/* @__PURE__ */ createElement("div", null));
}
const converter = (taskViewModel) => {
return /* @__PURE__ */ createElement("span", { class: "ellipsis secondary" }, taskViewModel.task.name);
};
return /* @__PURE__ */ createElement("div", { class: "month-grid-wrapper" }, /* @__PURE__ */ createElement("div", { class: "day-labels" }, ...dayLabels), /* @__PURE__ */ createElement("div", { class: "month-grid" }, ...offsetElements, ...Object.entries(monthGrid.days).map((entry) => {
const [date, mapState] = entry;
2024-07-26 03:38:46 +02:00
const isSelected = createProxyState(
[selectedDate],
() => selectedDate.value == parseInt(date)
);
function select() {
selectedDate.value = parseInt(date);
}
return /* @__PURE__ */ createElement("button", { class: "tile", "on:click": select, "toggle:selected": isSelected }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, date), /* @__PURE__ */ createElement(
2024-07-26 03:29:07 +02:00
"div",
{
class: "flex-column gap clip",
"children:append": [mapState, converter]
}
)));
})));
}
2024-07-26 03:14:55 +02:00
// src/View/ChatPages/calendarPage.tsx
function CalendarPage(calendarPageViewModel) {
calendarPageViewModel.loadData();
const mainContent = createProxyState(
[calendarPageViewModel.monthGrid],
() => {
if (calendarPageViewModel.monthGrid.value == void 0) {
return /* @__PURE__ */ createElement("div", null);
} else {
2024-07-26 03:38:46 +02:00
return MonthGrid2(
calendarPageViewModel.monthGrid.value,
calendarPageViewModel.selectedDate
);
2024-07-26 03:14:55 +02:00
}
}
);
return /* @__PURE__ */ createElement("div", { id: "calendar-page" }, /* @__PURE__ */ createElement("div", { class: "pane-wrapper" }, /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("span", null, /* @__PURE__ */ createElement(
"button",
{
class: "ghost",
"aria-label": translations.chatPage.calendar.todayButtonAudioLabel,
"on:click": calendarPageViewModel.showToday
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "today")
)), /* @__PURE__ */ createElement("span", null, /* @__PURE__ */ createElement(
"button",
{
class: "ghost",
"aria-label": translations.chatPage.calendar.previousMonthButtonAudioLabel,
"on:click": calendarPageViewModel.showPreviousMonth
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_back")
), /* @__PURE__ */ createElement("span", { class: "input-wrapper" }, /* @__PURE__ */ createElement(
"input",
{
class: "year-input",
type: "number",
"aria-label": translations.chatPage.calendar.yearInputAudioLabel,
placeholder: translations.chatPage.calendar.yearInputPlaceholder,
"bind:value": calendarPageViewModel.selectedYear
}
), /* @__PURE__ */ createElement(
"input",
{
class: "month-input",
type: "number",
"aria-label": translations.chatPage.calendar.monthInputAudioLabel,
placeholder: translations.chatPage.calendar.monthInputPlaceholder,
"bind:value": calendarPageViewModel.selectedMonth
}
)), /* @__PURE__ */ createElement(
"button",
{
class: "ghost",
"aria-label": translations.chatPage.calendar.nextMonthButtonAudioLabel,
"on:click": calendarPageViewModel.showNextMonth
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")
)), /* @__PURE__ */ createElement("span", null, /* @__PURE__ */ createElement(
"button",
{
class: "ghost",
"aria-label": translations.chatPage.task.createTaskButtonAudioLabel
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "add")
))), /* @__PURE__ */ createElement("div", { class: "content", "children:set": mainContent }))));
}
// src/View/Components/ribbonButton.tsx
function RibbonButton(label, icon, isSelected, select) {
return /* @__PURE__ */ createElement(
"button",
{
class: "ribbon-button",
"aria-label": label,
"toggle:selected": isSelected,
"on:click": select
},
/* @__PURE__ */ createElement("span", { class: "icon" }, icon)
);
}
// src/View/Components/chatViewToggleButton.tsx
function ChatViewToggleButton(label, icon, page, chatViewModel) {
function select() {
chatViewModel.selectedPage.value = page;
}
const isSelected = createProxyState(
[chatViewModel.selectedPage],
() => chatViewModel.selectedPage.value == page
);
return RibbonButton(label, icon, isSelected, select);
}
2024-07-23 13:09:46 +02:00
// src/View/Modals/chatMessageInfoModal.tsx
function ChatMessageInfoModal(chatMessageViewModel) {
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": chatMessageViewModel.isPresentingInfoModal }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.chatPage.message.messageInfoHeadline), /* @__PURE__ */ createElement("div", { class: "flex-column gap" }, /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "account_circle"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.message.sentBy), /* @__PURE__ */ createElement("b", { class: "break-word" }, chatMessageViewModel.sender))), /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "schedule"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.message.timeSent), /* @__PURE__ */ createElement("b", { class: "break-word" }, chatMessageViewModel.dateSent))), /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "forum"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.message.channel), /* @__PURE__ */ createElement("b", { class: "break-word" }, chatMessageViewModel.channel))), /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "description"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.message.messageContent), /* @__PURE__ */ createElement(
2024-07-21 13:13:41 +02:00
"b",
{
class: "break-word",
"subscribe:innerText": chatMessageViewModel.body
}
2024-07-23 13:09:46 +02:00
)))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-column gap" }, /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.copyMessage }, translations.chatPage.message.copyMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "content_copy")), /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.resendMessage }, translations.chatPage.message.resendMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "redo")), /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.decryptMessage }, translations.chatPage.message.decryptMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "key")))), /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.hideInfoModal }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
2024-07-21 13:13:41 +02:00
}
// src/View/Components/chatMessage.tsx
function ChatMessage2(chatMessageViewModel) {
const statusIcon = createProxyState(
[chatMessageViewModel.status],
() => {
switch (chatMessageViewModel.status.value) {
case "outbox" /* Outbox */:
return "hourglass_top";
case "sent" /* Sent */:
return "check";
case "received" /* Received */:
return "done_all";
default:
return "warning";
}
}
);
2024-07-21 13:13:41 +02:00
return /* @__PURE__ */ createElement(
"div",
{
class: "message-bubble",
"toggle:sentbyuser": chatMessageViewModel.sentByUser
},
2024-07-22 00:30:43 +02:00
/* @__PURE__ */ createElement("div", { class: "main tile" }, /* @__PURE__ */ createElement("div", { class: "text-container" }, /* @__PURE__ */ createElement("span", { class: "sender-name ellipsis" }, chatMessageViewModel.sender), /* @__PURE__ */ createElement(
2024-07-21 13:13:41 +02:00
"span",
{
class: "body",
"subscribe:innerText": chatMessageViewModel.body
}
), /* @__PURE__ */ createElement("span", { class: "timestamp ellipsis" }, /* @__PURE__ */ createElement("span", { class: "icon", "subscribe:innerText": statusIcon }), chatMessageViewModel.dateSent)), /* @__PURE__ */ createElement("div", { class: "button-container" }, /* @__PURE__ */ createElement(
2024-07-21 13:13:41 +02:00
"button",
{
2024-07-23 13:09:46 +02:00
"on:click": chatMessageViewModel.showInfoModal,
2024-07-21 13:13:41 +02:00
"aria-label": translations.chatPage.message.showMessageInfoButtonAudioLabel
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "info")
))),
2024-07-23 13:09:46 +02:00
ChatMessageInfoModal(chatMessageViewModel)
2024-07-21 13:13:41 +02:00
);
}
var ChatMessageViewModelToView = (chatMessageViewModel) => {
return ChatMessage2(chatMessageViewModel);
2024-07-21 13:13:41 +02:00
};
// src/View/ChatPages/messagePage.tsx
2024-07-22 21:07:44 +02:00
function MessagePage(messagePageViewModel) {
2024-07-23 00:05:48 +02:00
messagePageViewModel.loadData();
2024-07-21 01:12:35 +02:00
const messageContainer = /* @__PURE__ */ createElement(
"div",
{
id: "message-container",
"children:append": [
2024-07-22 21:07:44 +02:00
messagePageViewModel.chatMessageViewModels,
2024-07-21 01:12:35 +02:00
ChatMessageViewModelToView
]
}
);
function scrollDown() {
messageContainer.scrollTop = messageContainer.scrollHeight;
}
function scrollDownIfApplicable() {
const scrollFromBottom = messageContainer.scrollHeight - (messageContainer.scrollTop + messageContainer.offsetHeight);
if (scrollFromBottom > 400) return;
scrollDown();
}
2024-07-22 21:07:44 +02:00
messagePageViewModel.chatMessageViewModels.subscribeSilent(
scrollDownIfApplicable
);
2024-07-21 01:12:35 +02:00
setTimeout(() => scrollDown(), 100);
return /* @__PURE__ */ createElement("div", { id: "message-page" }, /* @__PURE__ */ createElement("div", { class: "pane-wrapper" }, /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("span", { class: "title" }, translations.chatPage.message.messagesHeadline)), /* @__PURE__ */ createElement("div", { class: "content" }, messageContainer, /* @__PURE__ */ createElement("div", { id: "composer" }, /* @__PURE__ */ createElement("div", { class: "content-width-constraint" }, /* @__PURE__ */ createElement("div", { class: "input-width-constraint" }, /* @__PURE__ */ createElement(
"input",
{
2024-07-22 21:07:44 +02:00
"bind:value": messagePageViewModel.composingMessage,
"on:enter": messagePageViewModel.sendMessage,
placeholder: translations.chatPage.message.composerInputPlaceholder
}
), /* @__PURE__ */ createElement(
"button",
{
class: "primary",
"aria-label": translations.chatPage.message.sendMessageButtonAudioLabel,
2024-07-22 21:07:44 +02:00
"on:click": messagePageViewModel.sendMessage,
"toggle:disabled": messagePageViewModel.cannotSendMessage
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "send")
))))))));
}
// src/View/Components/colorPicker.tsx
function ColorPicker(selectedColor) {
return /* @__PURE__ */ createElement("div", { class: "flex-row gap width-input" }, ...Object.values(Color).map((color) => {
const isSelected = createProxyState(
[selectedColor],
() => selectedColor.value == color
);
function setColor() {
selectedColor.value = color;
}
return /* @__PURE__ */ createElement(
"button",
{
color,
class: "fill-color width-100 flex",
style: "height: 2rem",
"toggle:selected": isSelected,
"on:click": setColor
}
);
}));
}
// src/View/Components/dangerousActionButton.tsx
function DangerousActionButton(label, icon, action) {
const isActionRequested = new State(false);
const cannotConfirm = createProxyState(
[isActionRequested],
() => isActionRequested.value == false
);
function requestAction() {
isActionRequested.value = true;
}
function abort() {
isActionRequested.value = false;
}
return /* @__PURE__ */ createElement("div", { class: "flex-row" }, /* @__PURE__ */ createElement("button", { class: "flex", "on:click": abort, "toggle:hidden": cannotConfirm }, translations.general.abortButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "undo")), /* @__PURE__ */ createElement(
"button",
{
class: "danger flex",
"on:click": requestAction,
"toggle:hidden": isActionRequested
},
label,
/* @__PURE__ */ createElement("span", { class: "icon" }, icon)
), /* @__PURE__ */ createElement(
"button",
{
class: "danger flex",
"on:click": action,
"toggle:hidden": cannotConfirm
},
translations.general.confirmButton,
/* @__PURE__ */ createElement("span", { class: "icon" }, "warning")
));
}
2024-07-20 18:49:05 +02:00
// src/View/Components/deletableListItem.tsx
function DeletableListItem(text, primaryButton, ondelete) {
2024-07-20 22:53:59 +02:00
return /* @__PURE__ */ createElement("div", { class: "tile flex-row justify-apart align-center padding-0" }, /* @__PURE__ */ createElement("span", { class: "padding-h ellipsis" }, text), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end" }, primaryButton, /* @__PURE__ */ createElement(
2024-07-20 18:49:05 +02:00
"button",
{
class: "danger",
"aria-label": translations.general.deleteItemButtonAudioLabel,
"on:click": ondelete
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "delete")
)));
}
2024-07-20 17:19:10 +02:00
// src/View/ChatPages/settingsPage.tsx
2024-07-22 21:07:44 +02:00
function SettingsPage(settingsPageViewModel) {
settingsPageViewModel.loadData();
2024-07-20 18:49:05 +02:00
const secondaryChannelConverter = (secondaryChannel) => {
return DeletableListItem(secondaryChannel, /* @__PURE__ */ createElement("span", null), () => {
2024-07-22 21:07:44 +02:00
settingsPageViewModel.removeSecondaryChannel(secondaryChannel);
2024-07-20 18:49:05 +02:00
});
};
return /* @__PURE__ */ createElement("div", { id: "settings-page" }, /* @__PURE__ */ createElement("div", { class: "pane-wrapper" }, /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("span", { class: "title" }, translations.chatPage.settings.settingsHeadline)), /* @__PURE__ */ createElement("div", { class: "content" }, /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "forum"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.settings.primaryChannelLabel), /* @__PURE__ */ createElement(
2024-07-20 17:30:36 +02:00
"input",
{
2024-07-22 21:07:44 +02:00
"bind:value": settingsPageViewModel.primaryChannelInput,
"on:enter": settingsPageViewModel.setPrimaryChannel
2024-07-20 17:30:36 +02:00
}
))), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end width-input" }, /* @__PURE__ */ createElement(
"button",
{
class: "width-50",
"aria-label": translations.chatPage.settings.setPrimaryChannelButtonAudioLabel,
2024-07-22 21:07:44 +02:00
"on:click": settingsPageViewModel.setPrimaryChannel,
"toggle:disabled": settingsPageViewModel.cannotSetPrimaryChannel
2024-07-20 17:30:36 +02:00
},
translations.general.setButton,
/* @__PURE__ */ createElement("span", { class: "icon" }, "check")
2024-07-20 18:49:05 +02:00
)), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-row width-input margin-bottom" }, /* @__PURE__ */ createElement(
"input",
{
"aria-label": translations.chatPage.settings.newSecondaryChannelAudioLabel,
placeholder: translations.chatPage.settings.newSecondaryChannelPlaceholder,
2024-07-22 21:07:44 +02:00
"bind:value": settingsPageViewModel.newSecondaryChannelInput,
"on:enter": settingsPageViewModel.addSecondaryChannel
2024-07-20 18:49:05 +02:00
}
), /* @__PURE__ */ createElement(
"button",
2024-07-20 18:10:42 +02:00
{
2024-07-20 18:49:05 +02:00
class: "primary",
"aria-label": translations.chatPage.settings.addSecondaryChannelButtonAudioLabel,
2024-07-22 21:07:44 +02:00
"on:click": settingsPageViewModel.addSecondaryChannel,
"toggle:disabled": settingsPageViewModel.cannotAddSecondaryChannel
2024-07-20 18:10:42 +02:00
},
2024-07-20 18:49:05 +02:00
/* @__PURE__ */ createElement("span", { class: "icon" }, "add")
)), /* @__PURE__ */ createElement(
"div",
{
class: "flex-column gap width-input",
"children:append": [
2024-07-22 21:07:44 +02:00
settingsPageViewModel.secondaryChannels,
2024-07-20 18:49:05 +02:00
secondaryChannelConverter
]
}
2024-07-20 19:18:19 +02:00
), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "key"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.settings.encryptionKeyLabel), /* @__PURE__ */ createElement(
"input",
{
2024-07-22 21:07:44 +02:00
"bind:value": settingsPageViewModel.encryptionKeyInput,
"on:enter": settingsPageViewModel.setEncryptionKey,
"set:type": settingsPageViewModel.encryptionKeyInputType
2024-07-20 19:18:19 +02:00
}
))), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end width-input" }, /* @__PURE__ */ createElement(
"button",
{
class: "width-50",
"aria-label": translations.chatPage.settings.setEncryptionKeyButtonAudioLabel,
2024-07-22 21:07:44 +02:00
"on:click": settingsPageViewModel.setEncryptionKey,
"toggle:disabled": settingsPageViewModel.cannotSetEncryptionKey
2024-07-20 19:18:19 +02:00
},
translations.general.setButton,
/* @__PURE__ */ createElement("span", { class: "icon" }, "check")
)), /* @__PURE__ */ createElement("label", { class: "inline" }, /* @__PURE__ */ createElement(
"input",
{
type: "checkbox",
2024-07-22 21:07:44 +02:00
"bind:checked": settingsPageViewModel.shouldShowEncryptionKey
2024-07-20 19:18:19 +02:00
}
), translations.chatPage.settings.showEncryptionKey), /* @__PURE__ */ createElement("hr", null), ColorPicker(settingsPageViewModel.color), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "width-input" }, DangerousActionButton(
2024-07-20 23:22:08 +02:00
translations.chatPage.settings.deleteChatButton,
"chat_error",
2024-07-22 21:07:44 +02:00
settingsPageViewModel.remove
))))));
2024-07-20 17:19:10 +02:00
}
2024-07-25 18:42:06 +02:00
// src/View/Components/propertyValueList.tsx
function PropertyValueList(propertyKey, stringEntryObjectConverter, objects, viewBuilder) {
const propertyValues = new ListState();
const sortedPropertyValues = createSortedPropertyValueState(propertyValues);
objects.subscribe(() => {
collectPropertyValuesToState(
propertyKey,
stringEntryObjectConverter,
objects,
propertyValues
);
});
return viewBuilder(propertyValues, sortedPropertyValues);
}
function collectPropertyValuesToState(propertyKey, stringEntryObjectConverter, objects, propertyValues) {
const values = collectObjectValuesForKey(
propertyKey,
stringEntryObjectConverter,
[...objects.value.values()]
);
for (const existingValue of values) {
if (propertyValues.value.has(existingValue)) continue;
propertyValues.add(existingValue);
}
for (const displayedValue of propertyValues.value.values()) {
if (values.includes(displayedValue) == false) {
propertyValues.remove(displayedValue);
}
}
}
function createSortedPropertyValueState(propertyValues) {
return createProxyState(
[propertyValues],
() => [...propertyValues.value.values()].sort(localeCompare)
);
}
function createPropertyValueIndexState(sortedKeys, key) {
return createProxyState(
[sortedKeys],
() => sortedKeys.value.indexOf(key)
);
}
2024-07-25 02:49:23 +02:00
// src/View/Components/filteredList.tsx
function FilteredList(reference, stringEntryObjectConverter, objects, viewBuilder) {
const matchingObjects = new ListState();
objects.handleAddition((newObject) => {
const doesMatch = checkDoesObjectMatchReference(
reference,
stringEntryObjectConverter(newObject)
);
if (doesMatch == false) return;
matchingObjects.add(newObject);
objects.handleRemoval(newObject, () => {
matchingObjects.remove(newObject);
});
});
return viewBuilder(matchingObjects);
}
2024-07-25 15:31:27 +02:00
// src/ViewModel/Utility/taskPropertyBulkChangeViewModel.ts
2024-07-25 02:49:23 +02:00
var TaskPropertyBulkChangeViewModel = class {
taskViewModels;
// state
inputValue = new State("");
// guards
cannotSet;
// methods
set = () => {
if (this.cannotSet.value == true) return;
this.taskViewModels.value.forEach((taskViewModel) => {
this.setValue(this.inputValue.value, taskViewModel);
});
};
setValue;
// init
constructor(taskViewModels, valueSetter, initialValue) {
this.taskViewModels = taskViewModels;
this.inputValue.value = initialValue;
this.setValue = valueSetter;
this.cannotSet = createProxyState(
[this.inputValue],
() => this.inputValue.value == "" || this.inputValue.value == initialValue
);
}
};
var TaskCategoryBulkChangeViewModel = class extends TaskPropertyBulkChangeViewModel {
constructor(taskViewModels, initialValue) {
super(
taskViewModels,
(newCategory, taskViewModel) => {
taskViewModel.category.value = newCategory;
taskViewModel.save();
},
initialValue
);
}
};
2024-07-25 04:12:30 +02:00
var TaskStatusBulkChangeViewModel = class extends TaskPropertyBulkChangeViewModel {
constructor(taskViewModels, initialValue) {
super(
taskViewModels,
(newStatus, taskViewModel) => {
taskViewModel.status.value = newStatus;
taskViewModel.save();
},
initialValue
);
}
};
2024-07-25 02:49:23 +02:00
// src/View/Components/taskEntry.tsx
function TaskEntry(taskViewModel) {
const details = {
description: taskViewModel.description.value || "---",
priority_high: taskViewModel.priority.value || "---",
category: taskViewModel.category.value || "---",
clock_loader_40: taskViewModel.status.value || "---",
calendar_month: taskViewModel.date.value || "---",
schedule: taskViewModel.time.value || "---"
};
2024-07-25 04:12:30 +02:00
const view = /* @__PURE__ */ createElement(
"button",
2024-07-25 02:49:23 +02:00
{
2024-07-25 16:20:43 +02:00
draggable: "true",
2024-07-25 04:12:30 +02:00
class: "tile flex-no",
2024-07-25 18:16:29 +02:00
style: "user-select: none; -webkit-user-select: none",
2024-07-25 16:20:43 +02:00
"on:click": taskViewModel.open,
"on:dragstart": taskViewModel.dragStart
2024-07-25 02:49:23 +02:00
},
2024-07-25 04:12:30 +02:00
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", { "subscribe:innerText": taskViewModel.name }), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement(
"div",
2024-07-25 02:49:23 +02:00
{
2024-07-25 04:12:30 +02:00
class: "grid secondary",
style: "grid-template-columns: repeat(2, 1fr); column-gap: 1rem; row-gap: .5rem"
2024-07-25 02:49:23 +02:00
},
2024-07-25 04:12:30 +02:00
...Object.entries(details).map((entry) => /* @__PURE__ */ createElement(
"span",
{
class: "flex-row align-center width-100 flex-no clip",
style: "gap: 1rem"
},
/* @__PURE__ */ createElement("span", { class: "icon", style: "font-size: 1.1rem" }, entry[0]),
/* @__PURE__ */ createElement("span", { class: "ellipsis" }, entry[1])
))
2024-07-25 02:49:23 +02:00
))
2024-07-25 04:12:30 +02:00
);
2024-07-25 02:49:23 +02:00
taskViewModel.index.subscribe((newIndex) => {
view.style.order = newIndex;
});
return view;
}
var TaskViewModelToEntry = (taskViewModel) => {
return TaskEntry(taskViewModel);
};
2024-07-25 01:38:11 +02:00
// src/View/ChatPages/boardKanbanPage.tsx
function BoardKanbanPage(boardViewModel) {
2024-07-25 02:49:23 +02:00
return PropertyValueList(
"category",
(taskViewModel) => taskViewModel.task,
boardViewModel.taskViewModels,
2024-07-25 18:42:06 +02:00
(categories, sortedCategories) => {
2024-07-25 18:25:05 +02:00
const categoryNameConverter = (categoryName) => {
2024-07-25 18:42:06 +02:00
const index = createPropertyValueIndexState(
sortedCategories,
categoryName
2024-07-25 18:25:05 +02:00
);
return KanbanBoard(categoryName, index, boardViewModel);
};
2024-07-25 02:49:23 +02:00
return /* @__PURE__ */ createElement(
"div",
{
class: "kanban-board-wrapper",
"children:append": [categories, categoryNameConverter]
}
);
}
);
}
2024-07-25 18:25:05 +02:00
function KanbanBoard(categoryName, index, boardViewModel) {
2024-07-25 02:49:23 +02:00
return FilteredList(
{ category: categoryName },
(taskViewModel) => taskViewModel.task,
2024-07-25 15:31:27 +02:00
boardViewModel.filteredTaskViewModels,
2024-07-25 02:49:23 +02:00
(taskViewModels) => {
const viewModel = new TaskCategoryBulkChangeViewModel(taskViewModels, categoryName);
2024-07-25 16:20:43 +02:00
function drop() {
boardViewModel.handleDropWithinBoard(categoryName);
2024-07-25 16:20:43 +02:00
}
2024-07-25 18:25:05 +02:00
const view = /* @__PURE__ */ createElement("div", { class: "flex-column flex-no", "on:dragover": allowDrop, "on:drop": drop }, /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
2024-07-25 02:49:23 +02:00
"input",
{
placeholder: translations.chatPage.task.renameCategoryInputPlaceholder,
"bind:value": viewModel.inputValue,
"on:enter": viewModel.set
}
), /* @__PURE__ */ createElement(
"button",
{
class: "primary",
"on:click": viewModel.set,
"toggle:disabled": viewModel.cannotSet
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "check")
)), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement(
"div",
{
2024-07-25 04:12:30 +02:00
class: "kanban-column",
2024-07-25 02:49:23 +02:00
"children:append": [taskViewModels, TaskViewModelToEntry]
}
));
2024-07-25 18:25:05 +02:00
index.subscribe((newIndex) => {
view.style.order = newIndex;
});
return view;
2024-07-25 02:49:23 +02:00
}
);
2024-07-25 01:38:11 +02:00
}
2024-07-23 13:09:46 +02:00
// src/View/Modals/boardSettingsModal.tsx
function BoardSettingsModal(boardViewModel) {
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": boardViewModel.isPresentingSettingsModal }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.chatPage.task.boardSettingsHeadline), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "label"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.boardNameInputLabel), /* @__PURE__ */ createElement(
"input",
{
"on:enter": boardViewModel.saveSettings,
"bind:value": boardViewModel.name
}
2024-07-23 14:31:50 +02:00
))), /* @__PURE__ */ createElement("hr", null), ColorPicker(boardViewModel.color), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "width-input" }, DangerousActionButton(
translations.chatPage.task.deleteBoardButton,
"delete_forever",
boardViewModel.deleteBoard
))), /* @__PURE__ */ createElement("button", { "on:click": boardViewModel.hideSettings }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
2024-07-23 13:09:46 +02:00
}
2024-07-25 04:12:30 +02:00
// src/View/ChatPages/boardStatusGridPage.tsx
function BoardStatusGridPage(boardViewModel) {
const statuses = new ListState();
2024-07-25 18:42:06 +02:00
const sortedStatuses = createSortedPropertyValueState(statuses);
2024-07-25 04:12:30 +02:00
boardViewModel.taskViewModels.subscribe(() => {
2024-07-25 18:42:06 +02:00
collectPropertyValuesToState(
2024-07-25 04:12:30 +02:00
"status",
(taskViewModel) => taskViewModel.task,
2024-07-25 18:42:06 +02:00
boardViewModel.taskViewModels,
statuses
2024-07-25 04:12:30 +02:00
);
});
2024-07-25 18:42:06 +02:00
const statusNameCellConverter = (statusName) => {
const index = createPropertyValueIndexState(sortedStatuses, statusName);
return StatusNameCell(statusName, index, boardViewModel);
};
2024-07-25 04:12:30 +02:00
return /* @__PURE__ */ createElement("div", { class: "status-page-content" }, /* @__PURE__ */ createElement(
"div",
{
class: "status-name-row",
"children:append": [statuses, statusNameCellConverter]
}
), PropertyValueList(
"category",
(taskViewModel) => taskViewModel.task,
2024-07-25 15:31:27 +02:00
boardViewModel.filteredTaskViewModels,
2024-07-25 18:42:06 +02:00
(categories, sortedCategories) => {
const categoryRowConverter = (categoryName) => {
const index = createPropertyValueIndexState(
sortedCategories,
categoryName
);
return CategoryRow(
categoryName,
index,
statuses,
sortedStatuses,
boardViewModel
);
};
2024-07-25 04:12:30 +02:00
return /* @__PURE__ */ createElement(
"div",
{
class: "status-grid-wrapper",
"children:append": [categories, categoryRowConverter]
}
);
}
));
}
2024-07-25 18:42:06 +02:00
function StatusNameCell(statusName, index, boardViewModel) {
2024-07-25 04:12:30 +02:00
const taskViewModelsWithMatchingStatus = new ListState();
boardViewModel.taskViewModels.handleAddition(
(taskViewModel) => {
const doesMatchStatus = taskViewModel.task.status == statusName;
if (doesMatchStatus == false) return;
taskViewModelsWithMatchingStatus.add(taskViewModel);
boardViewModel.taskViewModels.handleRemoval(taskViewModel, () => {
taskViewModelsWithMatchingStatus.remove(taskViewModel);
});
}
);
const viewModel = new TaskStatusBulkChangeViewModel(
taskViewModelsWithMatchingStatus,
statusName
);
2024-07-25 18:42:06 +02:00
const view = /* @__PURE__ */ createElement("div", { class: "flex-row" }, /* @__PURE__ */ createElement("div", { class: "property-input-wrapper" }, /* @__PURE__ */ createElement(
2024-07-25 04:12:30 +02:00
"input",
{
placeholder: translations.chatPage.task.renameCategoryInputPlaceholder,
"bind:value": viewModel.inputValue,
"on:enter": viewModel.set
}
), /* @__PURE__ */ createElement(
"button",
{
class: "primary",
"on:click": viewModel.set,
"toggle:disabled": viewModel.cannotSet
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "check")
)));
2024-07-25 18:42:06 +02:00
index.subscribe((newIndex) => {
view.style.order = newIndex;
});
return view;
2024-07-25 04:12:30 +02:00
}
2024-07-25 18:42:06 +02:00
function CategoryRow(categoryName, index, allStatuses, sortedStatuses, boardViewModel) {
2024-07-25 04:12:30 +02:00
return FilteredList(
{ category: categoryName },
(taskViewModel) => taskViewModel.task,
boardViewModel.taskViewModels,
(taskViewModels) => {
const statusNameConverter = (statusName) => {
2024-07-25 18:42:06 +02:00
const index2 = createPropertyValueIndexState(sortedStatuses, statusName);
2024-07-25 16:24:09 +02:00
return CategoryStatusColumn(
categoryName,
statusName,
2024-07-25 18:42:06 +02:00
index2,
2024-07-25 16:24:09 +02:00
boardViewModel,
taskViewModels
);
2024-07-25 04:12:30 +02:00
};
const viewModel = new TaskCategoryBulkChangeViewModel(taskViewModels, categoryName);
2024-07-25 18:42:06 +02:00
const view = /* @__PURE__ */ createElement("div", { class: "flex-row flex-no large-gap" }, /* @__PURE__ */ createElement("div", { class: "property-input-wrapper" }, /* @__PURE__ */ createElement(
2024-07-25 04:12:30 +02:00
"input",
{
placeholder: translations.chatPage.task.renameCategoryInputPlaceholder,
"bind:value": viewModel.inputValue,
"on:enter": viewModel.set
}
), /* @__PURE__ */ createElement(
"button",
{
class: "primary",
"on:click": viewModel.set,
"toggle:disabled": viewModel.cannotSet
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "check")
)), /* @__PURE__ */ createElement(
"div",
{
class: "flex-row large-gap padding-right",
"children:append": [allStatuses, statusNameConverter]
}
));
2024-07-25 18:42:06 +02:00
index.subscribe((newIndex) => {
view.style.order = newIndex;
});
return view;
2024-07-25 04:12:30 +02:00
}
);
}
2024-07-25 18:42:06 +02:00
function CategoryStatusColumn(categoryName, statusName, index, boardViewModel, taskViewModelsWithMatchingCategory) {
2024-07-25 04:12:30 +02:00
const taskViewModels = new ListState();
taskViewModelsWithMatchingCategory.handleAddition((taskViewModel) => {
const doesMatchStatus = taskViewModel.status.value == statusName;
if (doesMatchStatus == false) return;
taskViewModels.add(taskViewModel);
taskViewModelsWithMatchingCategory.handleRemoval(taskViewModel, () => {
taskViewModels.remove(taskViewModel);
});
});
2024-07-25 16:24:09 +02:00
function drop() {
boardViewModel.handleDropWithinBoard(categoryName, statusName);
2024-07-25 16:24:09 +02:00
}
2024-07-25 18:42:06 +02:00
const view = /* @__PURE__ */ createElement(
2024-07-25 04:12:30 +02:00
"div",
{
class: "status-column gap",
2024-07-25 16:24:09 +02:00
"on:dragover": allowDrop,
"on:drop": drop,
2024-07-25 04:12:30 +02:00
"children:append": [taskViewModels, TaskViewModelToEntry]
}
);
2024-07-25 18:42:06 +02:00
index.subscribe((newIndex) => {
view.style.order = newIndex;
});
return view;
2024-07-25 04:12:30 +02:00
}
2024-07-23 15:41:03 +02:00
// src/View/Components/boardViewToggleButton.tsx
function BoardViewToggleButton(label, icon, page, boardViewModel) {
function select() {
boardViewModel.selectedPage.value = page;
}
const isSelected = createProxyState(
[boardViewModel.selectedPage],
() => boardViewModel.selectedPage.value == page
);
return RibbonButton(label, icon, isSelected, select);
}
2024-07-25 15:31:27 +02:00
// src/ViewModel/Utility/searchViewModel.ts
var SearchViewModel = class {
// data
allObjects;
getStringsOfObject;
// state
appliedQuery = new State("");
searchInput = new State("");
matchingObjects;
// guards
cannotApplySearch = createProxyState(
[this.searchInput, this.appliedQuery],
() => this.searchInput.value == this.appliedQuery.value
);
// methods
applySearch = () => {
this.appliedQuery.value = this.searchInput.value;
console.log("applying search");
this.matchingObjects.clear();
for (const object of this.allObjects.value.values()) {
const doesMatch = this.checkDoesMatchSearch(object);
if (doesMatch == false) continue;
this.matchingObjects.add(object);
}
};
// init
constructor(allObjects, matchingObjects, getStringsOfObject) {
this.allObjects = allObjects;
this.matchingObjects = matchingObjects;
this.getStringsOfObject = getStringsOfObject;
this.allObjects.handleAddition((newObject) => {
const doesMatch = this.checkDoesMatchSearch(newObject);
if (doesMatch == false) {
this.matchingObjects.remove(newObject);
} else {
if (this.matchingObjects.value.has(newObject)) return;
this.matchingObjects.add(newObject);
this.allObjects.handleRemoval(newObject, () => {
this.matchingObjects.remove(newObject);
});
}
});
}
// utility
checkDoesMatchSearch = (object) => {
return checkDoesObjectMatchSearch(
this.appliedQuery.value,
this.getStringsOfObject,
object
);
};
};
// src/View/Modals/searchModal.tsx
function SearchModal(headline, allObjects, filteredObjects, converter, getStringsOfObject, isOpen) {
const viewModel = new SearchViewModel(
allObjects,
filteredObjects,
getStringsOfObject
);
function close() {
isOpen.value = false;
}
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isOpen }, /* @__PURE__ */ createElement("div", { style: "max-width: 64rem" }, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, headline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
"input",
{
placeholder: translations.general.searchLabel,
"bind:value": viewModel.searchInput,
"on:enter": viewModel.applySearch
}
), /* @__PURE__ */ createElement(
"button",
{
class: "primary",
"aria-label": translations.general.searchButtonAudioLabel,
"on:click": viewModel.applySearch,
"toggle:disabled": viewModel.cannotApplySearch
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "search")
)), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement(
"div",
{
class: "grid gap",
"children:append": [filteredObjects, converter]
}
)), /* @__PURE__ */ createElement("button", { "on:click": close }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
}
2024-07-24 15:57:02 +02:00
// src/View/Components/option.tsx
function Option(text, value, selectedOnCreate) {
return /* @__PURE__ */ createElement("option", { value, "toggle:selected": selectedOnCreate }, text);
}
var StringToOption = (string) => {
return Option(string, string, false);
};
var VersionIdToOption = (versionId) => {
const [date, rest] = versionId.split("T");
const [time] = rest.split(".");
const readableName = `${date} ${time}`;
return Option(readableName, versionId, false);
};
2024-07-24 13:31:28 +02:00
// src/View/Modals/taskSettingsModal.tsx
function TaskSettingsModal(taskViewModel) {
2024-07-24 15:57:02 +02:00
return /* @__PURE__ */ createElement("div", { class: "modal", open: true }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.chatPage.task.taskSettingsHeadline), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "history"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.general.fileVersionLabel), /* @__PURE__ */ createElement(
"select",
{
"bind:value": taskViewModel.selectedVersionId,
"children:append": [taskViewModel.versionIds, VersionIdToOption]
}
), /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_drop_down"))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "label"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskNameLabel), /* @__PURE__ */ createElement("input", { "bind:value": taskViewModel.name }))), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "description"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskDescriptionLabel), /* @__PURE__ */ createElement(
2024-07-24 13:59:42 +02:00
"textarea",
{
rows: "10",
"bind:value": taskViewModel.description
}
2024-07-24 14:10:09 +02:00
))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "category"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskCategoryLabel), /* @__PURE__ */ createElement("input", { "bind:value": taskViewModel.category }))), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "clock_loader_40"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskStatusLabel), /* @__PURE__ */ createElement("input", { "bind:value": taskViewModel.status }))), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "priority_high"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskPriorityLabel), /* @__PURE__ */ createElement("input", { type: "number", "bind:value": taskViewModel.priority }))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "calendar_month"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskDateLabel), /* @__PURE__ */ createElement("input", { type: "date", "bind:value": taskViewModel.date }))), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "schedule"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskTimeLabel), /* @__PURE__ */ createElement("input", { type: "time", "bind:value": taskViewModel.time }))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "width-input" }, DangerousActionButton(
translations.chatPage.task.deleteTaskButton,
"delete_forever",
taskViewModel.deleteTask
))), /* @__PURE__ */ createElement("div", { class: "flex-row width-100" }, /* @__PURE__ */ createElement("button", { class: "flex", "on:click": taskViewModel.close }, translations.general.closeButton), /* @__PURE__ */ createElement("button", { class: "flex primary", "on:click": taskViewModel.closeAndSave }, translations.general.saveButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "save")))));
2024-07-24 13:31:28 +02:00
}
// src/View/ChatPages/boardPage.tsx
function BoardPage(boardViewModel) {
boardViewModel.loadData();
2024-07-23 23:28:22 +02:00
const mainContent = createProxyState(
[boardViewModel.selectedPage],
() => {
switch (boardViewModel.selectedPage.value) {
2024-07-25 01:38:11 +02:00
case "kanban" /* Kanban */: {
return BoardKanbanPage(boardViewModel);
}
2024-07-25 04:12:30 +02:00
case "status-grid" /* StatusGrid */: {
return BoardStatusGridPage(boardViewModel);
}
2024-07-23 23:28:22 +02:00
default: {
2024-07-24 13:31:28 +02:00
return /* @__PURE__ */ createElement(
"div",
{
2024-07-24 15:59:48 +02:00
class: "task-grid",
2024-07-24 13:31:28 +02:00
"children:append": [
2024-07-25 15:31:27 +02:00
boardViewModel.filteredTaskViewModels,
2024-07-24 13:31:28 +02:00
TaskViewModelToEntry
]
}
);
2024-07-23 23:28:22 +02:00
}
}
}
);
2024-07-24 13:31:28 +02:00
const taskSettingsModal = createProxyState(
[boardViewModel.selectedTaskViewModel],
() => {
if (boardViewModel.selectedTaskViewModel.value == void 0) {
return /* @__PURE__ */ createElement("div", null);
} else {
return TaskSettingsModal(boardViewModel.selectedTaskViewModel.value);
}
}
);
return /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("span", null, /* @__PURE__ */ createElement(
2024-07-23 13:09:46 +02:00
"button",
{
class: "ghost",
"aria-label": translations.chatPage.task.closeBoard,
"on:click": boardViewModel.close
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_back")
), /* @__PURE__ */ createElement(
"button",
{
class: "ghost",
"aria-label": translations.chatPage.task.boardSettingsHeadline,
"on:click": boardViewModel.showSettings
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "settings")
2024-07-23 15:41:03 +02:00
)), /* @__PURE__ */ createElement("span", { class: "scroll-h ribbon" }, BoardViewToggleButton(
translations.chatPage.task.listViewButtonAudioLabel,
"view_list",
"list" /* List */,
boardViewModel
), BoardViewToggleButton(
translations.chatPage.task.kanbanViewButtonAudioLabel,
"view_kanban",
"kanban" /* Kanban */,
boardViewModel
), BoardViewToggleButton(
translations.chatPage.task.statusViewButtonAudioLabel,
"grid_view",
"status-grid" /* StatusGrid */,
boardViewModel
2024-07-23 15:25:16 +02:00
)), /* @__PURE__ */ createElement("span", null, /* @__PURE__ */ createElement(
"button",
{
class: "ghost",
"aria-label": translations.chatPage.task.filterTasksButtonAudioLabel,
"on:click": boardViewModel.showFilterModal
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "filter_alt")
), /* @__PURE__ */ createElement(
"button",
{
2024-07-23 23:28:22 +02:00
class: "ghost",
2024-07-23 15:25:16 +02:00
"aria-label": translations.chatPage.task.createTaskButtonAudioLabel,
2024-07-24 13:31:28 +02:00
"on:click": boardViewModel.createTask
2024-07-23 15:25:16 +02:00
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "add")
2024-07-25 15:31:27 +02:00
))), /* @__PURE__ */ createElement("div", { class: "content main-content", "children:set": mainContent }), BoardSettingsModal(boardViewModel), SearchModal(
translations.chatPage.task.filterTasksHeadline,
boardViewModel.taskViewModels,
boardViewModel.filteredTaskViewModels,
TaskViewModelToEntry,
2024-07-26 03:14:55 +02:00
TaskViewModel.getStringsForFilter,
2024-07-25 15:31:27 +02:00
boardViewModel.isPresentingFilterModal
), /* @__PURE__ */ createElement("div", { "children:set": taskSettingsModal }));
2024-07-24 13:31:28 +02:00
}
// src/View/Components/boardEntry.tsx
function BoardEntry(boardViewModel) {
const view = /* @__PURE__ */ createElement(
"button",
{
"set:color": boardViewModel.color,
class: "tile colored-tile",
"toggle:selected": boardViewModel.isSelected,
"on:click": boardViewModel.select,
"on:dragover": allowDrop,
"on:drop": boardViewModel.handleDropBetweenBoards
2024-07-24 13:31:28 +02:00
},
/* @__PURE__ */ createElement("span", { class: "shadow", "subscribe:innerText": boardViewModel.name }),
/* @__PURE__ */ createElement("b", { "subscribe:innerText": boardViewModel.name })
);
boardViewModel.index.subscribe((newIndex) => {
view.style.order = newIndex;
});
return view;
}
2024-07-24 13:31:28 +02:00
var BoardViewModelToEntry = (boardViewModel) => {
return BoardEntry(boardViewModel);
};
2024-07-22 13:17:21 +02:00
// src/View/ChatPages/taskPage.tsx
2024-07-22 21:07:44 +02:00
function TaskPage(taskPageViewModel) {
2024-07-23 00:05:48 +02:00
taskPageViewModel.loadData();
2024-07-23 23:32:49 +02:00
const isShowingBoard = createProxyState(
[taskPageViewModel.selectedBoardId],
() => taskPageViewModel.selectedBoardId.value != void 0
);
2024-07-23 02:57:20 +02:00
const paneContent = createProxyState(
2024-07-23 13:51:27 +02:00
[taskPageViewModel.selectedBoardId],
2024-07-23 02:57:20 +02:00
() => {
2024-07-23 13:51:27 +02:00
const selectedBoardId = taskPageViewModel.selectedBoardId.value;
if (selectedBoardId == void 0) {
return /* @__PURE__ */ createElement("div", { class: "pane align-center justify-center" }, /* @__PURE__ */ createElement("span", { class: "secondary" }, translations.chatPage.task.noBoardSelected));
}
const selectedBoard = taskPageViewModel.boardViewModels.value.get(selectedBoardId);
2024-07-23 02:57:20 +02:00
if (selectedBoard == void 0) {
2024-07-23 13:51:27 +02:00
return /* @__PURE__ */ createElement("div", { class: "pane align-center justify-center" }, /* @__PURE__ */ createElement("span", { class: "secondary" }, translations.chatPage.task.boardNotFound));
2024-07-23 02:57:20 +02:00
}
2024-07-23 13:51:27 +02:00
return BoardPage(selectedBoard);
2024-07-23 02:57:20 +02:00
}
);
2024-07-23 23:32:49 +02:00
return /* @__PURE__ */ createElement("div", { id: "task-page", "toggle:isshowingboard": isShowingBoard }, /* @__PURE__ */ createElement(
2024-07-23 00:05:48 +02:00
"div",
{
2024-07-23 23:32:49 +02:00
id: "board-list",
2024-07-23 15:25:16 +02:00
class: "pane-wrapper side background",
"set:color": taskPageViewModel.chatViewModel.displayedColor
},
/* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
"input",
{
"bind:value": taskPageViewModel.newBoardNameInput,
"on:enter": taskPageViewModel.createBoard,
placeholder: translations.chatPage.task.newBoardNamePlaceholder
}
), /* @__PURE__ */ createElement(
"button",
{
class: "primary",
"aria-label": translations.chatPage.task.createBoardButtonAudioLabel,
"on:click": taskPageViewModel.createBoard,
"toggle:disabled": taskPageViewModel.cannotCreateBoard
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "add")
))), /* @__PURE__ */ createElement("div", { class: "content" }, /* @__PURE__ */ createElement(
"div",
{
class: "grid gap",
style: "grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr))",
"children:append": [
taskPageViewModel.boardViewModels,
2024-07-24 13:31:28 +02:00
BoardViewModelToEntry
2024-07-23 15:25:16 +02:00
]
}
)))
2024-07-23 23:32:49 +02:00
), /* @__PURE__ */ createElement("div", { id: "board-content", class: "pane-wrapper", "children:set": paneContent }));
2024-07-22 13:17:21 +02:00
}
// src/View/chatPage.tsx
function ChatPage(chatViewModel) {
2024-07-23 21:52:16 +02:00
const mainContent = createProxyState(
[chatViewModel.selectedPage],
() => {
chatViewModel.closeSubPages();
switch (chatViewModel.selectedPage.value) {
case "settings" /* Settings */: {
return SettingsPage(chatViewModel.settingsPageViewModel);
}
case "tasks" /* Tasks */: {
return TaskPage(chatViewModel.taskPageViewModel);
}
case "calendar" /* Calendar */: {
return CalendarPage(chatViewModel.calendarViewModel);
}
2024-07-23 21:52:16 +02:00
default: {
return MessagePage(chatViewModel.messagePageViewModel);
}
2024-07-22 21:07:44 +02:00
}
2024-07-20 17:19:10 +02:00
}
2024-07-23 21:52:16 +02:00
);
2024-07-22 13:17:21 +02:00
return /* @__PURE__ */ createElement(
"article",
{
2024-07-22 13:17:21 +02:00
id: "chat-page",
2024-07-23 02:50:22 +02:00
"set:color": chatViewModel.displayedColor,
2024-07-22 13:17:21 +02:00
class: "subtle-background"
},
2024-07-22 13:17:21 +02:00
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("div", { id: "ribbon" }, /* @__PURE__ */ createElement(
"button",
{
class: "ghost",
"aria-label": translations.chatPage.closeChatAudioLabe,
"on:click": chatViewModel.close
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "close")
), /* @__PURE__ */ createElement("span", null, ChatViewToggleButton(
translations.chatPage.pages.calendar,
"calendar_month",
"calendar" /* Calendar */,
chatViewModel
), ChatViewToggleButton(
translations.chatPage.pages.tasks,
"task_alt",
"tasks" /* Tasks */,
chatViewModel
), ChatViewToggleButton(
translations.chatPage.pages.messages,
"forum",
"messages" /* Messages */,
chatViewModel
), ChatViewToggleButton(
translations.chatPage.pages.settings,
"settings",
"settings" /* Settings */,
chatViewModel
))), /* @__PURE__ */ createElement("div", { id: "main", "children:set": mainContent }))
);
}
// src/View/chatPageWrapper.tsx
function ChatPageWrapper(chatListViewModel2) {
const chatPageContent = createProxyState(
[chatListViewModel2.selectedChat],
() => {
if (chatListViewModel2.selectedChat.value == void 0) {
return /* @__PURE__ */ createElement("div", null);
} else {
return ChatPage(chatListViewModel2.selectedChat.value);
}
}
);
return /* @__PURE__ */ createElement(
"div",
{
id: "chat-page-wrapper",
"children:set": chatPageContent
}
);
}
2024-07-20 02:30:45 +02:00
// src/View/Modals/connectionModal.tsx
function ConnectionModal(connectionViewModel2) {
2024-07-20 03:34:54 +02:00
const previousAddressConverter = (address) => {
function connnect() {
connectionViewModel2.connectToAddress(address);
}
const cannotConnect = createProxyState(
[connectionViewModel2.isConnected],
2024-07-20 22:44:23 +02:00
() => connectionViewModel2.isConnected.value == true && connectionViewModel2.connectionModel.address == address
);
return DeletableListItem(
address,
/* @__PURE__ */ createElement(
"button",
{
class: "primary",
"on:click": connnect,
"toggle:disabled": cannotConnect,
"aria-label": translations.connectionModal.connectButtonAudioLabel
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "link")
),
() => {
connectionViewModel2.removePreviousAddress(address);
}
);
2024-07-20 03:34:54 +02:00
};
2024-07-20 02:30:45 +02:00
return /* @__PURE__ */ createElement(
"div",
{
class: "modal",
"toggle:open": connectionViewModel2.isShowingConnectionModal
},
2024-07-20 03:34:54 +02:00
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.connectionModal.connectionModalHeadline), /* @__PURE__ */ createElement(
"div",
{
class: "flex-column gap",
"children:append": [
connectionViewModel2.previousAddresses,
previousAddressConverter
]
}
2024-07-21 13:13:41 +02:00
)), /* @__PURE__ */ createElement("button", { "on:click": connectionViewModel2.hideConnectionModal }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close")))
2024-07-20 02:30:45 +02:00
);
}
2024-07-09 15:15:17 +02:00
// node_modules/udn-frontend/index.ts
var UDNFrontend = class {
ws;
// HANDLERS
connectionHandler = () => {
};
disconnectionHandler = () => {
};
messageHandler = (data) => {
};
2024-07-19 18:00:01 +02:00
mailboxHandler = (mailboxId) => {
2024-07-09 15:15:17 +02:00
};
2024-07-19 18:00:01 +02:00
mailboxConnectionHandler = (mailboxId) => {
2024-07-09 15:15:17 +02:00
};
2024-07-19 18:00:01 +02:00
mailboxDeleteHandler = (mailboxId) => {
2024-07-09 15:15:17 +02:00
};
// INIT
set onconnect(handler) {
this.connectionHandler = handler;
}
set ondisconnect(handler) {
this.disconnectionHandler = handler;
}
set onmessage(handler) {
this.messageHandler = handler;
}
set onmailboxcreate(handler) {
this.mailboxHandler = handler;
}
set onmailboxconnect(handler) {
this.mailboxConnectionHandler = handler;
}
set onmailboxdelete(handler) {
this.mailboxDeleteHandler = handler;
}
// UTILITY METHODS
send(messageObject) {
if (this.ws == void 0) return false;
if (this.ws.readyState != 1) return false;
2024-07-09 15:15:17 +02:00
const messageString = JSON.stringify(messageObject);
this.ws.send(messageString);
return true;
}
// PUBLIC METHODS
// connection
2024-07-19 18:17:00 +02:00
connect(address) {
2024-07-09 15:15:17 +02:00
try {
this.disconnect();
2024-07-19 18:17:00 +02:00
this.ws = new WebSocket(address);
2024-07-09 15:15:17 +02:00
this.ws.addEventListener("open", this.connectionHandler);
this.ws.addEventListener("close", this.disconnectionHandler);
this.ws.addEventListener("message", (message) => {
const dataString = message.data.toString();
const data = JSON.parse(dataString);
if (data.assignedMailboxId) {
return this.mailboxHandler(data.assignedMailboxId);
} else if (data.connectedMailboxId) {
return this.mailboxConnectionHandler(data.connectedMailboxId);
} else if (data.deletedMailbox) {
return this.mailboxDeleteHandler(data.deletedMailbox);
} else {
this.messageHandler(data);
}
});
} catch (error) {
console.error(error);
}
}
disconnect() {
this.ws?.close();
}
// message
sendMessage(channel, body) {
const messageObject = {
messageChannel: channel,
messageBody: body
};
return this.send(messageObject);
}
// subscription
subscribe(channel) {
const messageObject = {
subscribeChannel: channel
};
return this.send(messageObject);
}
unsubscribe(channel) {
const messageObject = {
unsubscribeChannel: channel
};
return this.send(messageObject);
}
// mailbox
requestMailbox() {
const messageObject = {
requestingMailboxSetup: true
};
return this.send(messageObject);
}
2024-07-19 18:00:01 +02:00
connectMailbox(mailboxId) {
2024-07-09 15:15:17 +02:00
const messageObject = {
2024-07-19 18:00:01 +02:00
requestedMailbox: mailboxId
2024-07-09 15:15:17 +02:00
};
return this.send(messageObject);
}
2024-07-19 18:00:01 +02:00
deleteMailbox(mailboxId) {
2024-07-09 15:15:17 +02:00
const messageObject = {
2024-07-19 18:00:01 +02:00
deletingMailbox: mailboxId
2024-07-09 15:15:17 +02:00
};
return this.send(messageObject);
}
};
2024-07-22 02:05:27 +02:00
// src/Model/Global/connectionModel.ts
2024-07-19 18:00:01 +02:00
var ConnectionModel = class {
udn;
storageModel;
2024-07-19 18:00:01 +02:00
// data
get isConnected() {
return this.udn.ws != void 0 && this.udn.ws.readyState == 1;
2024-07-13 17:13:59 +02:00
}
get address() {
2024-07-19 18:00:01 +02:00
return this.udn.ws?.url;
2024-07-13 17:13:59 +02:00
}
connectionChangeHandlerManager = new HandlerManager();
messageHandlerManager = new HandlerManager();
messageSentHandlerManager = new HandlerManager();
2024-07-21 01:12:35 +02:00
channelsToSubscribe = /* @__PURE__ */ new Set();
2024-07-22 03:01:04 +02:00
// handlers
2024-07-20 20:42:43 +02:00
handleMessage = (data) => {
this.messageHandlerManager.trigger(data);
2024-07-20 20:42:43 +02:00
};
handleConnectionChange = () => {
console.log("connection status:", this.isConnected, this.address);
this.connectionChangeHandlerManager.trigger();
if (this.isConnected == false) return;
2024-07-25 16:44:08 +02:00
if (this.address == void 0) return;
2024-07-21 01:12:35 +02:00
this.storeAddress(this.address);
this.sendSubscriptionRequest();
2024-07-20 22:44:23 +02:00
this.sendMessagesInOutbox();
};
2024-07-22 03:01:04 +02:00
// connection
connect = (address) => {
this.udn.connect(address);
};
disconnect = () => {
this.udn.disconnect();
const reconnectAddressPath = StorageModel.getPath(
2024-07-25 16:44:08 +02:00
"connection" /* ConnectionModel */,
2024-07-22 03:01:04 +02:00
filePaths.connectionModel.reconnectAddress
);
this.storageModel.remove(reconnectAddressPath);
};
2024-07-21 15:15:24 +02:00
// mailbox
2024-07-22 03:01:04 +02:00
getMailboxPath = (address) => {
const mailboxDirPath = StorageModel.getPath(
2024-07-25 16:44:08 +02:00
"connection" /* ConnectionModel */,
2024-07-22 03:01:04 +02:00
filePaths.connectionModel.mailboxes
);
const mailboxFilePath = [...mailboxDirPath, address];
return mailboxFilePath;
};
2024-07-21 15:15:24 +02:00
requestNewMailbox = () => {
2024-07-25 16:44:08 +02:00
console.log("requesting new mailbox");
2024-07-21 15:15:24 +02:00
this.udn.requestMailbox();
};
connectMailbox = () => {
if (this.address == void 0) return;
2024-07-22 03:01:04 +02:00
const mailboxId = this.storageModel.read(this.getMailboxPath(this.address));
2024-07-21 15:15:24 +02:00
console.log("connecting mailbox", mailboxId);
if (mailboxId == null) return this.requestNewMailbox();
this.udn.connectMailbox(mailboxId);
};
storeMailbox = (mailboxId) => {
if (this.address == void 0) return;
2024-07-22 03:01:04 +02:00
this.storageModel.write(this.getMailboxPath(this.address), mailboxId);
2024-07-21 15:15:24 +02:00
};
2024-07-21 01:12:35 +02:00
// subscription
addChannel = (channel) => {
this.channelsToSubscribe.add(channel);
this.sendSubscriptionRequest();
};
sendSubscriptionRequest = () => {
2024-07-21 03:41:10 +02:00
if (this.isConnected == false) return;
2024-07-21 01:12:35 +02:00
for (const channel of this.channelsToSubscribe) {
this.udn.subscribe(channel);
}
2024-07-21 15:15:24 +02:00
this.connectMailbox();
2024-07-21 01:12:35 +02:00
};
2024-07-20 20:42:43 +02:00
// outbox
2024-07-22 03:01:04 +02:00
getOutboxPath = () => {
return StorageModel.getPath(
2024-07-25 16:44:08 +02:00
"connection" /* ConnectionModel */,
2024-07-22 03:01:04 +02:00
filePaths.connectionModel.outbox
);
};
2024-07-20 20:42:43 +02:00
getOutboxMessags = () => {
2024-07-22 03:01:04 +02:00
const outboxPath = this.getOutboxPath();
2024-07-20 20:42:43 +02:00
const messageIds = this.storageModel.list(outboxPath);
2024-07-22 01:40:53 +02:00
let chatMessages = [];
2024-07-20 20:42:43 +02:00
for (const messageId of messageIds) {
2024-07-22 01:40:53 +02:00
const chatMessage = this.storageModel.readStringifiable(
[...outboxPath, messageId],
ChatMessageReference
);
if (chatMessage == null) continue;
chatMessages.push(chatMessage);
2024-07-20 20:42:43 +02:00
}
2024-07-22 01:40:53 +02:00
return chatMessages;
2024-07-20 20:42:43 +02:00
};
addToOutbox = (chatMessage) => {
2024-07-22 03:01:04 +02:00
const messagePath = [...this.getOutboxPath(), chatMessage.id];
2024-07-21 21:45:18 +02:00
this.storageModel.writeStringifiable(messagePath, chatMessage);
2024-07-20 20:42:43 +02:00
};
removeFromOutbox = (chatMessage) => {
2024-07-22 03:01:04 +02:00
const messagePath = [...this.getOutboxPath(), chatMessage.id];
2024-07-20 20:42:43 +02:00
this.storageModel.remove(messagePath);
};
sendMessagesInOutbox = () => {
const messages = this.getOutboxMessags();
for (const message of messages) {
const isSent = this.tryToSendMessage(message);
if (isSent == false) return;
this.removeFromOutbox(message);
}
};
2024-07-19 18:00:01 +02:00
// messaging
2024-07-20 20:42:43 +02:00
sendMessageOrStore = (chatMessage) => {
const isSent = this.tryToSendMessage(chatMessage);
if (isSent == true) return;
this.addToOutbox(chatMessage);
};
tryToSendMessage = (chatMessage) => {
2024-07-19 18:00:01 +02:00
const stringifiedBody = stringify(chatMessage);
const isSent = this.udn.sendMessage(
chatMessage.channel,
stringifiedBody
);
if (isSent) this.messageSentHandlerManager.trigger(chatMessage);
return isSent;
};
// storage
2024-07-22 03:01:04 +02:00
getPreviousAddressPath = () => {
return StorageModel.getPath(
2024-07-25 16:44:08 +02:00
"connection" /* ConnectionModel */,
2024-07-22 03:01:04 +02:00
filePaths.connectionModel.previousAddresses
);
};
getAddressPath = (address) => {
2024-07-22 03:01:04 +02:00
const dirPath = this.getPreviousAddressPath();
return [...dirPath, address];
};
2024-07-22 03:01:04 +02:00
getReconnectAddressPath = () => {
return StorageModel.getPath(
2024-07-25 16:44:08 +02:00
"connection" /* ConnectionModel */,
2024-07-22 03:01:04 +02:00
filePaths.connectionModel.reconnectAddress
);
};
storeAddress = (address) => {
const addressPath = this.getAddressPath(address);
2024-07-21 21:45:18 +02:00
this.storageModel.write(addressPath, "");
2024-07-22 03:01:04 +02:00
const reconnectAddressPath = this.getReconnectAddressPath();
2024-07-21 21:45:18 +02:00
this.storageModel.write(reconnectAddressPath, address);
};
removeAddress = (address) => {
const addressPath = this.getAddressPath(address);
this.storageModel.remove(addressPath);
};
2024-07-20 02:59:40 +02:00
get addresses() {
2024-07-22 03:01:04 +02:00
const dirPath = this.getPreviousAddressPath();
return this.storageModel.list(dirPath);
2024-07-20 02:59:40 +02:00
}
2024-07-22 03:01:04 +02:00
// init
2024-07-20 20:42:43 +02:00
constructor(storageModel2) {
2024-07-19 18:00:01 +02:00
this.udn = new UDNFrontend();
2024-07-20 20:42:43 +02:00
this.storageModel = storageModel2;
2024-07-19 18:17:00 +02:00
this.udn.onmessage = (data) => {
2024-07-20 20:42:43 +02:00
this.handleMessage(data);
2024-07-19 18:17:00 +02:00
};
this.udn.onconnect = () => {
this.handleConnectionChange();
};
this.udn.ondisconnect = () => {
this.handleConnectionChange();
};
2024-07-21 15:15:24 +02:00
this.udn.onmailboxcreate = (mailboxId) => {
console.log("created mailbox", mailboxId);
2024-07-21 15:15:24 +02:00
this.storeMailbox(mailboxId);
this.connectMailbox();
};
this.udn.onmailboxdelete = (mailboxId) => {
console.log(`mailbox ${mailboxId} deleted`);
2024-07-21 15:15:24 +02:00
this.requestNewMailbox();
};
this.udn.onmailboxconnect = (mailboxId) => {
console.log(`using mailbox ${mailboxId}`);
2024-07-21 15:15:24 +02:00
};
2024-07-22 03:02:33 +02:00
const reconnectAddressPath = this.getReconnectAddressPath();
2024-07-21 21:45:18 +02:00
const reconnectAddress = storageModel2.read(reconnectAddressPath);
2024-07-21 03:41:10 +02:00
if (reconnectAddress != null) {
this.connect(reconnectAddress);
}
2024-07-09 17:23:29 +02:00
}
};
2024-07-09 17:27:15 +02:00
2024-07-22 21:07:44 +02:00
// src/ViewModel/Global/connectionViewModel.ts
2024-07-20 02:18:15 +02:00
var ConnectionViewModel = class {
2024-07-25 16:20:43 +02:00
// init
constructor(coreViewModel, connectionModel2) {
this.coreViewModel = coreViewModel;
this.connectionModel = connectionModel2;
this.updatePreviousAddresses();
connectionModel2.connectionChangeHandlerManager.addHandler(
this.connectionChangeHandler
);
}
2024-07-20 02:18:15 +02:00
connectionModel;
// state
serverAddressInput = new State("");
isConnected = new State(false);
2024-07-20 02:30:45 +02:00
isShowingConnectionModal = new State(false);
previousAddresses = new ListState();
// guards
2024-07-20 02:18:15 +02:00
cannotConnect = createProxyState(
[this.serverAddressInput, this.isConnected],
() => this.isConnected.value == true && this.serverAddressInput.value == this.connectionModel.address || this.serverAddressInput.value == ""
);
cannotDisonnect = createProxyState(
[this.isConnected],
() => this.isConnected.value == false
);
hasNoPreviousConnections = createProxyState(
[this.previousAddresses],
() => this.previousAddresses.value.size == 0
);
2024-07-20 02:18:15 +02:00
// handlers
connectionChangeHandler = () => {
this.isConnected.value = this.connectionModel.isConnected;
if (this.connectionModel.isConnected == false) return;
2024-07-20 22:53:59 +02:00
if (this.connectionModel.address == void 0) return;
this.serverAddressInput.value = this.connectionModel.address;
if (!this.previousAddresses.value.has(this.connectionModel.address)) {
2024-07-20 22:44:23 +02:00
this.previousAddresses.add(this.connectionModel.address);
2024-07-20 02:18:15 +02:00
}
};
// methods
connect = () => {
this.connectToAddress(this.serverAddressInput.value);
};
connectToAddress = (address) => {
this.connectionModel.connect(address);
2024-07-20 02:18:15 +02:00
};
disconnect = () => {
this.connectionModel.disconnect();
};
2024-07-20 03:34:54 +02:00
removePreviousAddress = (address) => {
this.connectionModel.removeAddress(address);
this.updatePreviousAddresses();
};
2024-07-22 23:33:57 +02:00
// view
2024-07-20 02:30:45 +02:00
showConnectionModal = () => {
this.isShowingConnectionModal.value = true;
};
hideConnectionModal = () => {
this.isShowingConnectionModal.value = false;
};
2024-07-20 02:59:40 +02:00
updatePreviousAddresses = () => {
this.previousAddresses.clear();
this.previousAddresses.add(...this.connectionModel.addresses);
};
2024-07-25 16:20:43 +02:00
};
// src/ViewModel/Global/coreViewModel.ts
var CoreViewModel = class {
draggedObject = new State(void 0);
};
2024-07-19 23:45:07 +02:00
2024-07-20 05:10:50 +02:00
// src/View/Components/chatEntry.tsx
2024-07-20 14:16:26 +02:00
function ChatEntry(chatViewModel) {
2024-07-22 21:07:44 +02:00
const view = /* @__PURE__ */ createElement(
"button",
2024-07-20 14:16:26 +02:00
{
2024-07-23 00:09:39 +02:00
class: "tile colored-tile",
2024-07-25 16:20:43 +02:00
"set:color": chatViewModel.settingsPageViewModel.color,
2024-07-23 00:09:39 +02:00
style: "height: 8rem",
2024-07-22 21:07:44 +02:00
"on:click": chatViewModel.open
},
/* @__PURE__ */ createElement(
"span",
{
class: "shadow",
"subscribe:innerText": chatViewModel.settingsPageViewModel.primaryChannel
}
),
/* @__PURE__ */ createElement(
"h2",
{
"subscribe:innerText": chatViewModel.settingsPageViewModel.primaryChannel
}
)
);
2024-07-20 16:49:01 +02:00
chatViewModel.index.subscribe((newIndex) => {
view.style.order = newIndex;
});
return view;
2024-07-20 05:10:50 +02:00
}
2024-07-20 14:16:26 +02:00
var ChatViewModelToChatEntry = (chatViewModel) => {
return ChatEntry(chatViewModel);
2024-07-20 05:10:50 +02:00
};
2024-07-19 23:45:07 +02:00
// src/View/homePage.tsx
2024-07-21 17:55:53 +02:00
function HomePage(storageViewModel2, settingsViewModel2, connectionViewModel2, chatListViewModel2) {
2024-07-20 02:18:15 +02:00
const overviewSection = /* @__PURE__ */ createElement("div", { id: "overview-section" }, /* @__PURE__ */ createElement("h2", null, translations.homePage.overviewHeadline), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "cell_tower"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.homePage.serverAddress), /* @__PURE__ */ createElement(
"input",
{
list: "previous-connection-list",
2024-07-20 02:20:09 +02:00
placeholder: translations.homePage.serverAddressPlaceholder,
2024-07-20 02:18:15 +02:00
"bind:value": connectionViewModel2.serverAddressInput,
"on:enter": connectionViewModel2.connect
}
), /* @__PURE__ */ createElement(
"datalist",
{
hidden: true,
id: "previous-connection-list",
"children:append": [
connectionViewModel2.previousAddresses,
StringToOption
]
}
2024-07-20 02:18:15 +02:00
))), /* @__PURE__ */ createElement("div", { class: "flex-row" }, /* @__PURE__ */ createElement(
2024-07-20 01:14:21 +02:00
"button",
{
class: "danger flex justify-center",
2024-07-20 02:18:15 +02:00
"aria-label": translations.homePage.disconnectAudioLabel,
"on:click": connectionViewModel2.disconnect,
"toggle:disabled": connectionViewModel2.cannotDisonnect
2024-07-20 01:14:21 +02:00
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "link_off")
), /* @__PURE__ */ createElement(
"button",
{
class: "flex justify-center",
2024-07-20 02:30:45 +02:00
"aria-label": translations.homePage.manageConnectionsAudioLabel,
"on:click": connectionViewModel2.showConnectionModal,
"toggle:disabled": connectionViewModel2.hasNoPreviousConnections
2024-07-20 01:14:21 +02:00
},
2024-07-20 04:13:43 +02:00
/* @__PURE__ */ createElement("span", { class: "icon" }, "history")
2024-07-20 01:14:21 +02:00
), /* @__PURE__ */ createElement(
"button",
{
class: "primary flex justify-center",
2024-07-20 02:18:15 +02:00
"aria-label": translations.homePage.connectAudioLabel,
"on:click": connectionViewModel2.connect,
"toggle:disabled": connectionViewModel2.cannotConnect
2024-07-20 01:14:21 +02:00
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "link")
)), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "account_circle"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.homePage.yourNameLabel), /* @__PURE__ */ createElement(
"input",
{
2024-07-20 02:20:09 +02:00
placeholder: translations.homePage.yourNamePlaceholder,
2024-07-20 21:10:06 +02:00
"bind:value": settingsViewModel2.usernameInput,
"on:enter": settingsViewModel2.setName
}
))), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end" }, /* @__PURE__ */ createElement(
"button",
{
class: "width-50",
"on:click": settingsViewModel2.setName,
2024-07-20 17:30:36 +02:00
"toggle:disabled": settingsViewModel2.cannotSetName,
"aria-label": translations.homePage.setNameButtonAudioLabel
},
2024-07-20 17:30:36 +02:00
translations.general.setButton,
/* @__PURE__ */ createElement("span", { class: "icon" }, "check")
)), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "calendar_month"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.homePage.firstDayOfWeekLabel), /* @__PURE__ */ createElement("select", { "bind:value": settingsViewModel2.firstDayOfWeekInput }, ...translations.regional.weekdays.full.map(
(weekdayName, i) => Option(
weekdayName,
i.toString(),
2024-07-22 19:50:57 +02:00
i.toString() == settingsViewModel2.firstDayOfWeekInput.value
)
2024-07-21 17:55:53 +02:00
)), /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_drop_down"))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("button", { class: "tile flex-no", "on:click": storageViewModel2.showStorageModal }, /* @__PURE__ */ createElement("span", { class: "icon" }, "hard_drive_2"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.homePage.manageStorageButton))), /* @__PURE__ */ createElement("div", { class: "mobile-only" }, /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end" }, /* @__PURE__ */ createElement("button", { class: "ghost width-50", "on:click": scrollToChat }, translations.homePage.scrollToChatButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")))));
2024-07-20 00:15:05 +02:00
const chatSection = /* @__PURE__ */ createElement("div", { id: "chat-section" }, /* @__PURE__ */ createElement("h2", null, translations.homePage.chatsHeadline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
2024-07-19 23:45:07 +02:00
"input",
{
placeholder: translations.homePage.addChatPlaceholder,
2024-07-20 05:10:50 +02:00
"aria-label": translations.homePage.addChatAudioLabel,
"bind:value": chatListViewModel2.newChatPrimaryChannel,
"on:enter": chatListViewModel2.createChat
2024-07-19 23:45:07 +02:00
}
), /* @__PURE__ */ createElement(
"button",
{
class: "primary",
2024-07-20 05:10:50 +02:00
"aria-label": translations.homePage.addChatButton,
"on:click": chatListViewModel2.createChat,
"toggle:disabled": chatListViewModel2.cannotCreateChat
2024-07-19 23:45:07 +02:00
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "add")
2024-07-20 05:10:50 +02:00
)), /* @__PURE__ */ createElement(
"div",
{
2024-07-20 13:30:01 +02:00
id: "chat-grid",
2024-07-21 17:55:53 +02:00
"children:append": [
chatListViewModel2.chatViewModels,
ChatViewModelToChatEntry
]
2024-07-20 05:10:50 +02:00
}
));
2024-07-20 00:15:05 +02:00
function scrollToChat() {
chatSection.scrollIntoView();
}
2024-07-21 02:56:42 +02:00
return /* @__PURE__ */ createElement("article", { id: "home-page" }, /* @__PURE__ */ createElement("div", null, overviewSection, chatSection));
2024-07-19 23:45:07 +02:00
}
2024-07-22 02:05:27 +02:00
// src/Model/Global/settingsModel.ts
var SettingsModel = class {
storageModel;
2024-07-22 03:01:04 +02:00
// data
username;
firstDayOfWeek;
2024-07-22 03:01:04 +02:00
// storage
setName(newValue) {
this.username = newValue;
2024-07-22 03:01:04 +02:00
const path = StorageModel.getPath(
2024-07-25 16:44:08 +02:00
"settings" /* SettingsModel */,
2024-07-22 03:01:04 +02:00
filePaths.settingsModel.username
);
2024-07-21 21:45:18 +02:00
this.storageModel.write(path, newValue);
}
setFirstDayOfWeek(newValue) {
this.firstDayOfWeek = newValue;
2024-07-22 03:01:04 +02:00
const path = StorageModel.getPath(
2024-07-25 16:44:08 +02:00
"settings" /* SettingsModel */,
2024-07-22 03:01:04 +02:00
filePaths.settingsModel.firstDayOfWeek
);
2024-07-22 01:13:20 +02:00
this.storageModel.write(path, newValue);
}
2024-07-21 21:45:18 +02:00
// load
loadUsernam() {
2024-07-22 03:01:04 +02:00
const path = StorageModel.getPath(
2024-07-25 16:44:08 +02:00
"settings" /* SettingsModel */,
2024-07-22 03:01:04 +02:00
filePaths.settingsModel.username
);
2024-07-21 21:45:18 +02:00
const content = this.storageModel.read(path);
this.username = content ?? "";
}
2024-07-21 21:45:18 +02:00
loadFirstDayofWeek() {
2024-07-22 03:01:04 +02:00
const path = StorageModel.getPath(
2024-07-25 16:44:08 +02:00
"settings" /* SettingsModel */,
2024-07-22 03:01:04 +02:00
filePaths.settingsModel.firstDayOfWeek
);
2024-07-22 01:13:20 +02:00
const content = this.storageModel.read(path);
this.firstDayOfWeek = content ?? "0";
}
// init
constructor(storageModel2) {
this.storageModel = storageModel2;
2024-07-21 21:45:18 +02:00
this.loadUsernam();
this.loadFirstDayofWeek();
}
};
2024-07-22 21:07:44 +02:00
// src/ViewModel/Global/settingsViewModel.ts
var SettingsViewModel = class {
2024-07-25 16:20:43 +02:00
// init
constructor(coreViewModel, settingsModel2) {
this.coreViewModel = coreViewModel;
this.settingsModel = settingsModel2;
this.username.value = settingsModel2.username;
this.usernameInput.value = settingsModel2.username;
this.firstDayOfWeekInput.value = settingsModel2.firstDayOfWeek;
this.firstDayOfWeekInput.subscribe(this.setFirstDayofWeek);
}
settingsModel;
// state
2024-07-20 21:10:06 +02:00
username = new State("");
usernameInput = new State("");
2024-07-22 01:13:20 +02:00
firstDayOfWeekInput = new State("0");
// guards
cannotSetName = createProxyState(
2024-07-20 21:10:06 +02:00
[this.usernameInput],
() => this.usernameInput.value == "" || this.usernameInput.value == this.settingsModel.username
);
2024-07-22 23:33:57 +02:00
// methods
setName = () => {
2024-07-20 21:10:06 +02:00
this.settingsModel.setName(this.usernameInput.value);
this.username.value = this.settingsModel.username;
this.usernameInput.callSubscriptions();
};
setFirstDayofWeek = () => {
this.settingsModel.setFirstDayOfWeek(this.firstDayOfWeekInput.value);
};
};
2024-07-21 17:55:53 +02:00
// src/View/Components/directoryItemList.tsx
function DirectoryItemList(storageViewModel2, pathString = PATH_COMPONENT_SEPARATOR) {
const StringToDirectoryItemList = (pathString2) => DirectoryItemList(storageViewModel2, pathString2);
2024-07-21 17:55:53 +02:00
const path = StorageModel.stringToPathComponents(pathString);
2024-07-21 18:56:56 +02:00
const fileName = StorageModel.getFileName(path);
2024-07-21 17:55:53 +02:00
const items = new ListState();
2024-07-22 19:28:39 +02:00
const style = `text-indent: ${path.length}rem`;
2024-07-21 17:55:53 +02:00
function loadItems() {
items.clear();
const directoryItems = storageViewModel2.storageModel.list(path);
2024-07-21 17:55:53 +02:00
for (const directoryItem of directoryItems) {
const itemPath = [...path, directoryItem];
const pathString2 = StorageModel.pathComponentsToString(...itemPath);
items.add(pathString2);
}
}
function select() {
storageViewModel2.selectedPath.value = pathString;
2024-07-21 17:55:53 +02:00
}
storageViewModel2.lastDeletedItemPath.subscribe((lastDeletedItemPath) => {
if (!items.value.has(lastDeletedItemPath)) return;
select();
setTimeout(() => loadItems(), 50);
});
2024-07-21 17:55:53 +02:00
const isSelected = createProxyState(
[storageViewModel2.selectedPath],
() => storageViewModel2.selectedPath.value == pathString
2024-07-21 17:55:53 +02:00
);
isSelected.subscribe(() => {
if (isSelected.value == false) return;
loadItems();
});
return /* @__PURE__ */ createElement("div", { class: "flex-column" }, /* @__PURE__ */ createElement(
"button",
{
class: "width-100 flex-1 clip",
"toggle:selected": isSelected,
"on:click": select
},
/* @__PURE__ */ createElement("span", { class: "ellipsis width-100 flex-1", style }, fileName)
), /* @__PURE__ */ createElement(
"div",
{
class: "flex-column",
"children:append": [items, StringToDirectoryItemList]
}
));
}
// src/View/Components/fileBrowser.tsx
function FileBrowser(storageViewModel2) {
const detailView = createProxyState(
[storageViewModel2.selectedPath],
() => {
if (storageViewModel2.selectedPath.value == PATH_COMPONENT_SEPARATOR)
return /* @__PURE__ */ createElement("span", { class: "secondary" }, translations.storage.noItemSelected);
return /* @__PURE__ */ createElement("div", { class: "flex-column gap" }, /* @__PURE__ */ createElement("div", { class: "tile flex-no" }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, translations.storage.path), /* @__PURE__ */ createElement(
"span",
{
class: "break-all",
"subscribe:innerText": storageViewModel2.selectedPath
}
))), /* @__PURE__ */ createElement("div", { class: "tile flex-no" }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, translations.storage.content), /* @__PURE__ */ createElement(
"code",
{
"subscribe:innerText": storageViewModel2.selectedFileContent
}
))), DangerousActionButton(
translations.storage.deleteItem,
"delete_forever",
storageViewModel2.deleteSelectedItem
));
}
2024-07-21 18:56:56 +02:00
);
const view = /* @__PURE__ */ createElement("div", { class: "file-browser" }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("div", { class: "scroll-area" }, DirectoryItemList(storageViewModel2)), /* @__PURE__ */ createElement("div", { class: "detail-button-wrapper" }, /* @__PURE__ */ createElement("button", { class: "ghost", "on:click": scrollToDetails }, /* @__PURE__ */ createElement(
2024-07-21 18:56:56 +02:00
"span",
{
class: "ellipsis",
"subscribe:innerText": storageViewModel2.selectedFileName
2024-07-21 18:56:56 +02:00
}
2024-07-21 19:44:10 +02:00
), /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")))), /* @__PURE__ */ createElement("div", { class: "scroll-area", "children:set": detailView }));
2024-07-21 18:56:56 +02:00
function scrollToDetails() {
view.scrollLeft = view.scrollWidth;
}
return view;
2024-07-21 17:55:53 +02:00
}
// src/View/Modals/storageModal.tsx
function StorageModal(storageViewModel2) {
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": storageViewModel2.isShowingStorageModal }, /* @__PURE__ */ createElement("div", { style: "max-width: 64rem" }, /* @__PURE__ */ createElement("main", { class: "padding-0" }, FileBrowser(storageViewModel2)), /* @__PURE__ */ createElement("button", { "on:click": storageViewModel2.hideStorageModal }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
2024-07-21 17:55:53 +02:00
}
2024-07-22 21:07:44 +02:00
// src/ViewModel/Global/storageViewModel.ts
2024-07-21 17:55:53 +02:00
var StorageViewModel = class {
2024-07-25 16:20:43 +02:00
// init
constructor(coreViewModel, storageModel2) {
this.coreViewModel = coreViewModel;
this.storageModel = storageModel2;
this.selectedFileName = createProxyState(
[this.selectedPath],
() => StorageModel.getFileNameFromString(this.selectedPath.value)
);
this.selectedFileContent = createProxyState(
[this.selectedPath],
() => this.getSelectedItemContent()
);
}
2024-07-21 17:55:53 +02:00
storageModel;
// state
isShowingStorageModal = new State(false);
2024-07-21 19:44:10 +02:00
selectedPath = new State(PATH_COMPONENT_SEPARATOR);
didMakeChanges = new State(false);
selectedFileName;
selectedFileContent;
lastDeletedItemPath = new State("");
// methods
getSelectedItemContent = () => {
const path = StorageModel.stringToPathComponents(this.selectedPath.value);
2024-07-21 21:45:18 +02:00
const content = this.storageModel.read(path);
return (content ?? translations.storage.notAFile) || translations.storage.contentEmpty;
};
deleteSelectedItem = () => {
const path = StorageModel.stringToPathComponents(this.selectedPath.value);
this.lastDeletedItemPath.value = this.selectedPath.value;
2024-07-22 01:40:53 +02:00
this.storageModel.removeRecursively(path);
this.didMakeChanges.value = true;
};
2024-07-22 23:33:57 +02:00
// view
2024-07-21 17:55:53 +02:00
showStorageModal = () => {
this.isShowingStorageModal.value = true;
};
hideStorageModal = () => {
2024-07-21 19:44:10 +02:00
if (this.didMakeChanges.value == true) {
window.location.reload();
return;
2024-07-21 19:44:10 +02:00
}
2024-07-21 17:55:53 +02:00
this.isShowingStorageModal.value = false;
};
};
2024-07-19 19:23:48 +02:00
// src/index.tsx
var storageModel = new StorageModel();
2024-07-20 20:42:43 +02:00
var settingsModel = new SettingsModel(storageModel);
var connectionModel = new ConnectionModel(storageModel);
var chatListModel = new ChatListModel(
storageModel,
settingsModel,
connectionModel
);
2024-07-25 16:20:43 +02:00
var coreVieWModel = new CoreViewModel();
var storageViewModel = new StorageViewModel(coreVieWModel, storageModel);
var settingsViewModel = new SettingsViewModel(coreVieWModel, settingsModel);
var connectionViewModel = new ConnectionViewModel(
coreVieWModel,
connectionModel
);
2024-07-20 21:10:06 +02:00
var chatListViewModel = new ChatListViewModel(
2024-07-25 16:20:43 +02:00
coreVieWModel,
2024-07-20 21:10:06 +02:00
storageModel,
2024-07-24 01:19:44 +02:00
chatListModel,
2024-07-20 21:10:06 +02:00
settingsViewModel
);
2024-07-20 23:20:57 +02:00
chatListViewModel.selectedChat.subscribe(() => {
document.body.toggleAttribute(
"showing-chat",
chatListViewModel.selectedChat.value != void 0
);
});
2024-07-21 02:49:04 +02:00
document.body.append(
2024-07-22 01:40:53 +02:00
/* @__PURE__ */ createElement("div", { id: "background-wrapper" }, /* @__PURE__ */ createElement("div", { id: "sky" }), /* @__PURE__ */ createElement("div", { id: "grass-1" }), /* @__PURE__ */ createElement("div", { id: "grass-2" }))
2024-07-21 02:49:04 +02:00
);
2024-07-20 02:30:45 +02:00
document.querySelector("main").append(
2024-07-21 17:55:53 +02:00
HomePage(
storageViewModel,
settingsViewModel,
connectionViewModel,
chatListViewModel
),
2024-07-20 14:16:26 +02:00
ChatPageWrapper(chatListViewModel),
2024-07-21 17:55:53 +02:00
ConnectionModal(connectionViewModel),
StorageModal(storageViewModel)
2024-07-20 02:30:45 +02:00
);
2024-07-07 13:43:18 +02:00
})();