This commit is contained in:
marlon-erler
2026-09-26 23:32:38 +02:00
parent ce9f6d89f0
commit 9622c196cd
40 changed files with 2866 additions and 2706 deletions
+6 -2
View File
@@ -7,11 +7,11 @@ import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
import * as React from "../../react";
import { Colors } from "../../colors";
import { CommonKeys } from "../../View/keystrokes";
import { PinchToZoomData } from "../../Model/Utility/utility";
import BoardsAndTasksModel, {
BoardInfoFileContent,
TaskFileContent,
} from "../../Model/Files/boardsAndTasksModel";
import {PinchToZoomData} from "../../Model/Utility/utility";
export default class BoardViewModel extends TaskContainingPageViewModel {
// state
@@ -34,7 +34,11 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
new React.ListState();
isFilterActive: React.State<boolean>;
pinchToZoomData: React.State<PinchToZoomData> = new React.State({zoom: 1, x: 0, y: 0});
pinchToZoomData: React.State<PinchToZoomData> = new React.State({
zoom: 1,
x: 0,
y: 0,
});
// paths
getBasePath = (): string[] => {
+43 -43
View File
@@ -4,11 +4,19 @@ import CoreViewModel from "../Global/coreViewModel";
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
import * as React from "../../react";
import { CommonKeys } from "../../View/keystrokes";
import {
formatISO,
formatISOFromParts,
formatMonthStringFromParts,
formatTime,
isoToDateString,
isoToMonthString,
padZero,
} from "../../Model/Utility/utility";
import CalendarModel, { MonthGrid } from "../../Model/Files/calendarModel";
import BoardsAndTasksModel, {
TaskFileContent,
} from "../../Model/Files/boardsAndTasksModel";
import {formatISO, formatISOFromParts, formatMonthStringFromParts, formatTime, isoToDateString, isoToMonthString, padZero} from "../../Model/Utility/utility";
export const CALENDAR_EVENT_BOARD_ID = "events";
@@ -22,7 +30,11 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
}
get selectedDateString(): string {
return formatISOFromParts(this.selectedYear.value, this.selectedMonth.value, this.selectedDate.value);
return formatISOFromParts(
this.selectedYear.value,
this.selectedMonth.value,
this.selectedDate.value,
);
}
// paths
@@ -39,8 +51,8 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
monthGrid: React.State<
MonthGrid<React.MapState<TaskViewModel>> | undefined
> = new React.State<any>(undefined);
nextTask = new React.State<TaskViewModel|undefined>(undefined);
nextTask = new React.State<TaskViewModel | undefined>(undefined);
// methods
createEvent = (): void => {
@@ -65,9 +77,7 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
};
getEventsForDate = (): React.MapState<TaskViewModel> | undefined => {
const paddedDate: string = padZero(
this.selectedDate.toString(), 2
);
const paddedDate: string = padZero(this.selectedDate.toString(), 2);
if (this.monthGrid.value == undefined) {
return undefined;
@@ -82,9 +92,7 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
): React.MapState<TaskViewModel> | null => {
if (this.monthGrid.value == null) return null;
const date: string = isoToDateString(
taskFileContent.date ?? "",
);
const date: string = isoToDateString(taskFileContent.date ?? "");
return this.monthGrid.value.days[date];
};
@@ -152,11 +160,7 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
};
handleDrop = (year: string, month: string, date: string): void => {
const ISOString: string = formatISOFromParts(
year,
month,
date,
);
const ISOString: string = formatISOFromParts(year, month, date);
const draggedObject: any = this.coreViewModel.draggedObject.value;
if (draggedObject instanceof TaskViewModel == false) return;
@@ -165,37 +169,34 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
};
handleChron = (): void => {
this.updateMonthGrid();
this.updateNextTask();
}
this.updateMonthGrid();
this.updateNextTask();
};
updateMonthGrid = (): void => {
const today = formatISO(this.coreViewModel.todayDate.value)
if (
this.currentTodayDate == today
)
return;
const today = formatISO(this.coreViewModel.todayDate.value);
if (this.currentTodayDate == today) return;
this.loadMonthTasks();
this.currentTodayDate = today;
this.selectedDate.callSubscriptions();
this.selectedDate.callSubscriptions();
};
updateNextTask = (): void => {
if (this.selectedDateString != this.currentTodayDate) {
this.nextTask.value = undefined;
return;
}
if (this.selectedDateString != this.currentTodayDate) {
this.nextTask.value = undefined;
return;
}
const tasks = this.getEventsForDate();
if (tasks == undefined) return;
const taskArray =[...tasks.value.values()];
const sorted = taskArray.sort((a, b) => a.index.value - b.index.value);
for (const task of sorted) {
if (task.time.value <= this.coreViewModel.time.value) continue;
this.nextTask.value = task;
break;
};
}
const tasks = this.getEventsForDate();
if (tasks == undefined) return;
const taskArray = [...tasks.value.values()];
const sorted = taskArray.sort((a, b) => a.index.value - b.index.value);
for (const task of sorted) {
if (task.time.value <= this.coreViewModel.time.value) continue;
this.nextTask.value = task;
break;
}
};
// load
loadMonthTasks = (): void => {
@@ -217,8 +218,7 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
}
};
loadData = (): void => {
};
loadData = (): void => {};
// init
constructor(
@@ -237,9 +237,9 @@ export default class CalendarPageViewModel extends TaskContainingPageViewModel {
React.bulkSubscribe([this.selectedYear, this.selectedMonth], () => {
this.loadMonthTasks();
});
this.selectedDate.subscribeSilent(()=>{
this.updateNextTask();
})
this.selectedDate.subscribeSilent(() => {
this.updateNextTask();
});
// handlers
boardsAndTasksModel.taskHandlerManager.setHandler(
+134 -125
View File
@@ -16,9 +16,17 @@ export default class MessagePageViewModel extends Context {
new React.MapState();
filteredMessageViewModels: React.ListState<ChatMessageViewModel> =
new React.ListState();
replyViewSelectedMessage = new React.State<ChatMessageViewModel|undefined>(undefined);
isReplyViewActive: React.State<boolean> = React.createProxyState([this.replyViewSelectedMessage], ()=>this.replyViewSelectedMessage.value != undefined);
isReplyViewInactive = React.createProxyState([this.isReplyViewActive], ()=>!this.isReplyViewActive.value);
replyViewSelectedMessage = new React.State<
ChatMessageViewModel | undefined
>(undefined);
isReplyViewActive: React.State<boolean> = React.createProxyState(
[this.replyViewSelectedMessage],
() => this.replyViewSelectedMessage.value != undefined,
);
isReplyViewInactive = React.createProxyState(
[this.isReplyViewActive],
() => !this.isReplyViewActive.value,
);
searchViewModel: SearchViewModel<ChatMessageViewModel>;
isFilterModalOpen = new React.State<boolean>(false);
reactionFilter = new React.State<ReactionSymbols | undefined>(undefined);
@@ -39,193 +47,194 @@ export default class MessagePageViewModel extends Context {
if (this.cannotSendMessage.value == true) return;
const idPromise = this.sendMessageFromBody(this.composingMessage.value);
this.composingMessage.value = "";
this.composingMessage.value = "";
const id = await idPromise;
if (id && this.reactionFilter.value != undefined) {
this.sendReaction(id, this.reactionFilter.value, false);
}
const id = await idPromise;
if (id && this.reactionFilter.value != undefined) {
this.sendReaction(id, this.reactionFilter.value, false);
}
};
sendMessageFromBody = (body: string): Promise<string|false>=> {
let replyId: string | undefined = undefined;
if (this.replyingMessage.value) {
replyId = this.replyingMessage.value.chatMessage.id;
}
const id = this.chatViewModel.chatModel.sendMessage(body, replyId);
sendMessageFromBody = (body: string): Promise<string | false> => {
let replyId: string | undefined = undefined;
if (this.replyingMessage.value) {
replyId = this.replyingMessage.value.chatMessage.id;
}
const id = this.chatViewModel.chatModel.sendMessage(body, replyId);
if (this.replyViewSelectedMessage.value == undefined)
this.replyingMessage.value = undefined;
if (this.replyViewSelectedMessage.value == undefined)
this.replyingMessage.value = undefined;
return id;
return id;
};
decryptMessage = async (
messageViewModel: ChatMessageViewModel,
messageViewModel: ChatMessageViewModel,
): Promise<void> => {
const chatMessage: ChatMessage = messageViewModel.chatMessage;
await this.chatViewModel.chatModel.decryptMessage(chatMessage);
this.chatViewModel.chatModel.addMessage(chatMessage);
messageViewModel.loadData();
const chatMessage: ChatMessage = messageViewModel.chatMessage;
await this.chatViewModel.chatModel.decryptMessage(chatMessage);
this.chatViewModel.chatModel.addMessage(chatMessage);
messageViewModel.loadData();
};
sendReaction = (
messageId: string,
content: ReactionSymbols,
isDeleting: boolean,
messageId: string,
content: ReactionSymbols,
isDeleting: boolean,
): void => {
this.chatViewModel.chatModel.sendReaction(
messageId,
content,
isDeleting,
);
this.chatViewModel.chatModel.sendReaction(
messageId,
content,
isDeleting,
);
};
setReply = (
chatMessageViewModel: ChatMessageViewModel | undefined,
setFocus: boolean = true,
chatMessageViewModel: ChatMessageViewModel | undefined,
setFocus: boolean = true,
): void => {
this.replyingMessage.value = chatMessageViewModel;
if (setFocus == false || chatMessageViewModel == undefined) return;
this.setFocus();
this.replyingMessage.value = chatMessageViewModel;
if (setFocus == false || chatMessageViewModel == undefined) return;
this.setFocus();
};
resetReply = (setFocus: boolean = true): void => {
this.replyingMessage.value = undefined;
if (setFocus == false) return;
this.setFocus();
this.replyingMessage.value = undefined;
if (setFocus == false) return;
this.setFocus();
};
// view
showChatMessage = (chatMessage: ChatMessage): void => {
const chatMessageViewModel = new ChatMessageViewModel(
this.coreViewModel,
this,
chatMessage,
chatMessage.sender ==
this.chatViewModel.settingsViewModel.username.value,
);
const chatMessageViewModel = new ChatMessageViewModel(
this.coreViewModel,
this,
chatMessage,
chatMessage.sender ==
this.chatViewModel.settingsViewModel.username.value,
);
const existingChatMessageViewModel: ChatMessageViewModel | undefined =
this.chatMessageViewModels.value.get(chatMessage.id);
if (existingChatMessageViewModel != undefined) {
existingChatMessageViewModel.body.value = chatMessage.body;
existingChatMessageViewModel.status.value = chatMessage.status;
} else {
this.chatMessageViewModels.set(
chatMessage.id,
chatMessageViewModel,
);
}
const existingChatMessageViewModel: ChatMessageViewModel | undefined =
this.chatMessageViewModels.value.get(chatMessage.id);
if (existingChatMessageViewModel != undefined) {
existingChatMessageViewModel.body.value = chatMessage.body;
existingChatMessageViewModel.status.value = chatMessage.status;
} else {
this.chatMessageViewModels.set(
chatMessage.id,
chatMessageViewModel,
);
}
};
handleReaction = (reaction: ChatMessageReaction): void => {
const messageViewModel: ChatMessageViewModel | undefined =
this.chatMessageViewModels.value.get(reaction.messageId);
if (messageViewModel == undefined) return;
messageViewModel.handleReaction(reaction);
const messageViewModel: ChatMessageViewModel | undefined =
this.chatMessageViewModels.value.get(reaction.messageId);
if (messageViewModel == undefined) return;
messageViewModel.handleReaction(reaction);
};
showFilterModal = (): void => {
this.isFilterModalOpen.value = true;
this.isFilterModalOpen.value = true;
};
hideFilterModal = (): void => {
this.isFilterModalOpen.value = false;
this.isFilterModalOpen.value = false;
};
revokeReactionFilter = (persist: boolean = true): void => {
this.reactionFilter.value = undefined;
if (persist) this.chatViewModel.chatModel.storeFilter("");
this.reactionFilter.value = undefined;
if (persist) this.chatViewModel.chatModel.storeFilter("");
};
setReactionFilter = (content: ReactionSymbols): void => {
this.reactionFilter.value = content;
this.chatViewModel.chatModel.storeFilter(content);
this.reactionFilter.value = content;
this.chatViewModel.chatModel.storeFilter(content);
};
resetFilter = (): void => {
this.revokeReactionFilter();
this.searchViewModel.search("");
this.revokeReactionFilter();
this.searchViewModel.search("");
};
setReplyView = (message: ChatMessageViewModel): void => {
this.replyViewSelectedMessage.value = message;
this.revokeReactionFilter(false);
this.setReply(message, false);
}
this.replyViewSelectedMessage.value = message;
this.revokeReactionFilter(false);
this.setReply(message, false);
};
resetReplyView = (): void => {
this.replyViewSelectedMessage.value = undefined;
this.resetReply(false);
this.restoreFilter();
}
this.replyViewSelectedMessage.value = undefined;
this.resetReply(false);
this.restoreFilter();
};
setFocus = (): void => {
this.focusSetter.callSubscriptions();
this.focusSetter.callSubscriptions();
};
// load
loadData = (): void => {
this.chatMessageViewModels.clear();
for (const chatMessage of this.chatViewModel.chatModel.messages) {
this.showChatMessage(chatMessage);
}
for (const reaction of this.chatViewModel.chatModel.reactions) {
this.handleReaction(reaction);
}
this.chatMessageViewModels.clear();
for (const chatMessage of this.chatViewModel.chatModel.messages) {
this.showChatMessage(chatMessage);
}
for (const reaction of this.chatViewModel.chatModel.reactions) {
this.handleReaction(reaction);
}
};
restoreFilter = (): void => {
const previousFilter = this.chatViewModel.chatModel.getFilter() as any;
if (Object.values(ReactionSymbols).includes(previousFilter)) this.setReactionFilter(previousFilter);
}
const previousFilter = this.chatViewModel.chatModel.getFilter() as any;
if (Object.values(ReactionSymbols).includes(previousFilter))
this.setReactionFilter(previousFilter);
};
// init
constructor(
public readonly coreViewModel: CoreViewModel,
public readonly chatViewModel: ChatViewModel,
public readonly coreViewModel: CoreViewModel,
public readonly chatViewModel: ChatViewModel,
) {
super("message-page");
this.restoreFilter();
super("message-page");
this.restoreFilter();
// states
this.cannotSendMessage = React.createProxyState(
[
this.chatViewModel.settingsViewModel.username,
this.composingMessage,
],
() =>
this.chatViewModel.settingsViewModel.username.value == "" ||
this.composingMessage.value == "",
);
// states
this.cannotSendMessage = React.createProxyState(
[
this.chatViewModel.settingsViewModel.username,
this.composingMessage,
],
() =>
this.chatViewModel.settingsViewModel.username.value == "" ||
this.composingMessage.value == "",
);
this.searchViewModel = new SearchViewModel(
this.chatMessageViewModels,
this.filteredMessageViewModels,
(chatMessageViewModel) => [chatMessageViewModel.body.value],
new React.ListState(),
);
this.searchViewModel = new SearchViewModel(
this.chatMessageViewModels,
this.filteredMessageViewModels,
(chatMessageViewModel) => [chatMessageViewModel.body.value],
new React.ListState(),
);
this.isFilterActive = React.createProxyState(
[this.searchViewModel.appliedQuery, this.reactionFilter],
() =>
this.searchViewModel.appliedQuery.value != "" ||
this.reactionFilter.value != undefined,
);
this.isFilterActive = React.createProxyState(
[this.searchViewModel.appliedQuery, this.reactionFilter],
() =>
this.searchViewModel.appliedQuery.value != "" ||
this.reactionFilter.value != undefined,
);
// keystrokes
this.registerKeyStroke(CommonKeys.Filter, this.showFilterModal);
this.registerKeyStroke(CommonKeys.CloseOrCancel, () => {
if (this.isFilterModalOpen.value == true) {
this.hideFilterModal();
} else {
this.replyingMessage.value = undefined;
}
});
this.registerKeyStroke(CommonKeys.Reset, this.resetFilter);
this.registerKeyStroke(CommonKeys.Create, this.setFocus);
// keystrokes
this.registerKeyStroke(CommonKeys.Filter, this.showFilterModal);
this.registerKeyStroke(CommonKeys.CloseOrCancel, () => {
if (this.isFilterModalOpen.value == true) {
this.hideFilterModal();
} else {
this.replyingMessage.value = undefined;
}
});
this.registerKeyStroke(CommonKeys.Reset, this.resetFilter);
this.registerKeyStroke(CommonKeys.Create, this.setFocus);
this.chatViewModel.registerContext(ChatPageTypes.Messages, this);
this.chatViewModel.registerContext(ChatPageTypes.Messages, this);
}
}
+11 -16
View File
@@ -30,8 +30,7 @@ export default class SettingsPageViewModel extends Context {
);
cannotSetColor: React.State<boolean> = React.createProxyState(
[this.color, this.appliedColor],
() =>
this.color.value == this.appliedColor.value,
() => this.color.value == this.appliedColor.value,
);
cannotAddSecondaryChannel: React.State<boolean> = React.createProxyState(
[this.newSecondaryChannelInput],
@@ -41,11 +40,8 @@ export default class SettingsPageViewModel extends Context {
// methods
setName = (): void => {
this.chatViewModel.chatModel.setName(
this.nameInput.value,
);
this.name.value =
this.chatViewModel.chatModel.info.name;
this.chatViewModel.chatModel.setName(this.nameInput.value);
this.name.value = this.chatViewModel.chatModel.info.name;
this.chatViewModel.chatListViewModel.updateIndices();
};
@@ -79,7 +75,7 @@ export default class SettingsPageViewModel extends Context {
applyColor = (): void => {
this.chatViewModel.setColor(this.color.value);
this.appliedColor.value = this.color.value;
this.appliedColor.value = this.color.value;
};
remove = (): void => {
@@ -90,18 +86,17 @@ export default class SettingsPageViewModel extends Context {
// load
preloadData = (): void => {
this.name.value =
this.chatViewModel.chatModel.info.name;
this.name.value = this.chatViewModel.chatModel.info.name;
this.color.value = this.chatViewModel.chatModel.color;
this.appliedColor.value = this.color.value;
this.appliedColor.value = this.color.value;
};
updateData = (): void => {
this.preloadData();
this.nameInput.value = this.name.value;
this.chatViewModel.resetColor();
}
this.preloadData();
this.nameInput.value = this.name.value;
this.chatViewModel.resetColor();
};
loadData = (): void => {
this.nameInput.value = this.name.value;
+7 -2
View File
@@ -178,8 +178,13 @@ export default class TaskPageViewModel extends ContextHost<string> {
this.chatViewModel = chatViewModel;
// filter
implementFilter(this.boardViewModels, this.boardMatches, this.boardQuery, (board) => board.name.value);
// filter
implementFilter(
this.boardViewModels,
this.boardMatches,
this.boardQuery,
(board) => board.name.value,
);
// context
this.chatViewModel.registerContext(ChatPageTypes.Tasks, this);
+6 -6
View File
@@ -1,4 +1,5 @@
import TaskContainingPageViewModel from "./taskContainingPageViewModel";
import CalendarPageViewModel from "./calendarPageViewModel";
import CoreViewModel, { Context } from "../Global/coreViewModel";
import ChatViewModel from "../Chat/chatViewModel";
import * as React from "../../react";
@@ -8,7 +9,6 @@ import { localeCompare, padZero } from "../../Model/Utility/utility";
import BoardsAndTasksModel, {
TaskFileContent,
} from "../../Model/Files/boardsAndTasksModel";
import CalendarPageViewModel from "./calendarPageViewModel";
export default class TaskViewModel extends Context {
// util
@@ -200,11 +200,11 @@ export default class TaskViewModel extends Context {
this.loadTaskData();
this.loadVersionIds();
if (this.containingViewModel instanceof CalendarPageViewModel) {
this.containingViewModel.nextTask.subscribe((nextTask)=>{
this.isNotNext.value = nextTask != this;
})
}
if (this.containingViewModel instanceof CalendarPageViewModel) {
this.containingViewModel.nextTask.subscribe((nextTask) => {
this.isNotNext.value = nextTask != this;
});
}
};
loadTaskData = (): void => {