diff --git a/src/View/Components/boardViewToggleButton.tsx b/src/View/Components/boardViewToggleButton.tsx index 0a592718..ec3bf4c9 100644 --- a/src/View/Components/boardViewToggleButton.tsx +++ b/src/View/Components/boardViewToggleButton.tsx @@ -12,7 +12,8 @@ export function BoardViewToggleButton( key: string, ) { function select() { - boardViewModel.selectedPage.value = page; + boardViewModel.selectedPage.value = page; + boardViewModel.resetPinchZoom(); } const isSelected = React.createProxyState( diff --git a/src/View/chatPage.css b/src/View/chatPage.css index f79cbb27..269bb596 100644 --- a/src/View/chatPage.css +++ b/src/View/chatPage.css @@ -232,11 +232,13 @@ body[showing-chat] #chat-page-wrapper { overflow-x: auto; padding: 0px; + overflow-y: hidden; } #chat-page > div > #main > div > .pane-wrapper > .pane > .toolbar > span { display: flex; align-items: center; + overflow-y: hidden; } #chat-page > div > #main > div > .pane-wrapper > .pane > .toolbar > span.title { diff --git a/src/View/homePage.tsx b/src/View/homePage.tsx index 9042ad94..1e0feb39 100644 --- a/src/View/homePage.tsx +++ b/src/View/homePage.tsx @@ -21,6 +21,8 @@ export function HomePage( ) { const greeting = React.createProxyState([settingsViewModel.username], ()=>coreViewModel.translations.homePage.greeting(settingsViewModel.username.value)); + const isChatPlaceholderHidden = React.createProxyState([chatListViewModel.chatMatches, chatListViewModel.chatQuery], ()=>chatListViewModel.chatMatches.value.size > 0 || chatListViewModel.chatQuery.value != ""); + // sections const overviewSection = (
@@ -139,6 +141,13 @@ export function HomePage( {NewItemEntry(coreViewModel, chatListViewModel.chatQuery, chatListViewModel.createChat)} + + {coreViewModel.translations.homePage.noChatsPlaceholder} + +
`Aktualisieren: ${version}`, typeChat: "Chat", - - addChatPlaceholder: "Chat hinzufügen", - addChatButton: "Chat hinzufügen", + noChatsPlaceholder: "Keine Chats", }, settings: { @@ -658,7 +654,7 @@ export const allTranslations: { task: { noBoardSelected: "Kein Board ausgewählt", boardNotFound: "Board nicht gefunden", - + typeBoard: "Board", closeBoardButtonAudioLabel: "board schließen", @@ -748,7 +744,7 @@ export const allTranslations: { saveButton: "Guardar", setButton: "OK", - filterOrCreateLabel: (type: string) => `Buscar o crear ${type}`, + filterOrCreateLabel: (type: string) => `Buscar o crear ${type}`, createLabel: (query: string) => `Crear "${query}"`, reloadAppButton: "Recargar app", @@ -793,11 +789,9 @@ export const allTranslations: { transferDataButton: "Enviar o exportar archivos", updateButton: (version: string) => `Actualizar a ${version}`, - - typeChat: "chat", - addChatPlaceholder: "Añadir chat", - addChatButton: "Añadir chat", + typeChat: "chat", + noChatsPlaceholder: "No hay chats", }, settings: { @@ -1004,7 +998,7 @@ export const allTranslations: { task: { noBoardSelected: "Ningún tablero seleccionado", boardNotFound: "Tablero no encontrado", - + typeBoard: "board", closeBoardButtonAudioLabel: "cerrar tablero", diff --git a/src/ViewModel/Pages/boardViewModel.ts b/src/ViewModel/Pages/boardViewModel.ts index c95ae353..bd205615 100644 --- a/src/ViewModel/Pages/boardViewModel.ts +++ b/src/ViewModel/Pages/boardViewModel.ts @@ -21,7 +21,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel { index: React.State = new React.State(0); selectedPage: React.State = new React.State( - BoardPageTypes.List, + BoardPageTypes.List, ); isSelected: React.State; @@ -31,331 +31,339 @@ export default class BoardViewModel extends TaskContainingPageViewModel { searchViewModel: SearchViewModel; searchSuggestions = new React.ListState(); filteredTaskViewModels: React.ListState = - new React.ListState(); + new React.ListState(); isFilterActive: React.State; pinchToZoomData: React.State = new React.State({ - zoom: 1, - x: 0, - y: 0, + zoom: 1, + x: 0, + y: 0, }); // paths getBasePath = (): string[] => { - return [ - ...this.taskPageViewModel.getBoardViewPath(this.boardInfo.fileId), - ]; + return [ + ...this.taskPageViewModel.getBoardViewPath(this.boardInfo.fileId), + ]; }; getLastUsedBoardPath = (): string[] => { - return [...this.getBasePath(), BoardViewModelSubPaths.LastUsedView]; + return [...this.getBasePath(), BoardViewModelSubPaths.LastUsedView]; }; getPreviousSearchesPath = (): string[] => { - return [...this.getBasePath(), BoardViewModelSubPaths.PreviousSearches]; + return [...this.getBasePath(), BoardViewModelSubPaths.PreviousSearches]; }; getLastSearchPath = (): string[] => { - return [...this.getBasePath(), BoardViewModelSubPaths.LastSearch]; + return [...this.getBasePath(), BoardViewModelSubPaths.LastSearch]; }; // settings saveSettings = (): void => { - const newBoardInfoFileContent: BoardInfoFileContent = - BoardsAndTasksModel.createBoardInfoFileContent( - this.boardInfo.fileId, - this.name.value, - this.color.value, - ); + const newBoardInfoFileContent: BoardInfoFileContent = + BoardsAndTasksModel.createBoardInfoFileContent( + this.boardInfo.fileId, + this.name.value, + this.color.value, + ); - this.taskPageViewModel.updateBoard(newBoardInfoFileContent); + this.taskPageViewModel.updateBoard(newBoardInfoFileContent); }; applyColor = (): void => { - this.taskPageViewModel.chatViewModel.setDisplayedColor( - this.color.value, - ); + this.taskPageViewModel.chatViewModel.setDisplayedColor( + this.color.value, + ); }; deleteBoard = (): void => { - this.taskPageViewModel.deleteBoard(this.boardInfo); - this.chatViewModel.taskBoardSuggestions.remove(this.boardInfo.fileId); - this.close(); + this.taskPageViewModel.deleteBoard(this.boardInfo); + this.chatViewModel.taskBoardSuggestions.remove(this.boardInfo.fileId); + this.close(); }; // methods createTask = (): void => { - this.createTaskFromBoardId(this.boardInfo.fileId); + this.createTaskFromBoardId(this.boardInfo.fileId); }; handleDropWithinBoard = (category?: string, status?: string): void => { - const draggedObject: any = this.coreViewModel.draggedObject.value; - if (draggedObject instanceof TaskViewModel == false) return; - draggedObject.setCategoryAndStatus(category, status); + const draggedObject: any = this.coreViewModel.draggedObject.value; + if (draggedObject instanceof TaskViewModel == false) return; + draggedObject.setCategoryAndStatus(category, status); }; handleDropBetweenBoards = (): void => { - const draggedObject: any = this.coreViewModel.draggedObject.value; - if (draggedObject instanceof TaskViewModel == false) return; - draggedObject.setBoardId(this.boardInfo.fileId); + const draggedObject: any = this.coreViewModel.draggedObject.value; + if (draggedObject instanceof TaskViewModel == false) return; + draggedObject.setBoardId(this.boardInfo.fileId); }; // storage storeLastUsedView = (): void => { - const path: string[] = this.getLastUsedBoardPath(); - const lastUsedView: string = this.selectedPage.value; - this.coreViewModel.storageModel.write(path, lastUsedView); + const path: string[] = this.getLastUsedBoardPath(); + const lastUsedView: string = this.selectedPage.value; + this.coreViewModel.storageModel.write(path, lastUsedView); }; restoreLastUsedView = (): void => { - const path: string[] = this.getLastUsedBoardPath(); - const lastUsedView: string | null = - this.coreViewModel.storageModel.read(path); - if (lastUsedView == null) return; + const path: string[] = this.getLastUsedBoardPath(); + const lastUsedView: string | null = + this.coreViewModel.storageModel.read(path); + if (lastUsedView == null) return; - this.selectedPage.value = lastUsedView as BoardPageTypes; + this.selectedPage.value = lastUsedView as BoardPageTypes; }; handleNewSearch = (searchTerm: string): void => { - const suggestionPath: string[] = [ - ...this.getPreviousSearchesPath(), - searchTerm, - ]; - this.coreViewModel.storageModel.write(suggestionPath, ""); - if (!this.searchSuggestions.value.has(searchTerm) && searchTerm != "") { - this.searchSuggestions.add(searchTerm); - } + const suggestionPath: string[] = [ + ...this.getPreviousSearchesPath(), + searchTerm, + ]; + this.coreViewModel.storageModel.write(suggestionPath, ""); + if (!this.searchSuggestions.value.has(searchTerm) && searchTerm != "") { + this.searchSuggestions.add(searchTerm); + } - const lastSearchPath: string[] = this.getLastSearchPath(); - this.coreViewModel.storageModel.write(lastSearchPath, searchTerm); + const lastSearchPath: string[] = this.getLastSearchPath(); + this.coreViewModel.storageModel.write(lastSearchPath, searchTerm); }; handleSearchRemoved = (searchTerm: string): void => { - const suggestionPath: string[] = [ - ...this.getPreviousSearchesPath(), - searchTerm, - ]; - this.coreViewModel.storageModel.remove(suggestionPath); + const suggestionPath: string[] = [ + ...this.getPreviousSearchesPath(), + searchTerm, + ]; + this.coreViewModel.storageModel.remove(suggestionPath); }; // view showTask = (taskFileContent: TaskFileContent): void => { - if (taskFileContent.boardId != this.boardInfo.fileId) { - // remove task that was moved to different board - this.boardsAndTasksModel.deleteTaskReference( - this.boardInfo.fileId, - taskFileContent.fileId, - ); - this.removeTaskFromView(taskFileContent); - return; - } + if (taskFileContent.boardId != this.boardInfo.fileId) { + // remove task that was moved to different board + this.boardsAndTasksModel.deleteTaskReference( + this.boardInfo.fileId, + taskFileContent.fileId, + ); + this.removeTaskFromView(taskFileContent); + return; + } - const taskViewModel: TaskViewModel = new TaskViewModel( - this.coreViewModel, - this.chatViewModel, - this.boardsAndTasksModel, - this, - taskFileContent, - ); - this.taskViewModels.set(taskFileContent.fileId, taskViewModel); + const taskViewModel: TaskViewModel = new TaskViewModel( + this.coreViewModel, + this.chatViewModel, + this.boardsAndTasksModel, + this, + taskFileContent, + ); + this.taskViewModels.set(taskFileContent.fileId, taskViewModel); }; removeTaskFromView = (taskFileContent: TaskFileContent): void => { - this.taskViewModels.remove(taskFileContent.fileId); - this.updateIndex(); + this.taskViewModels.remove(taskFileContent.fileId); + this.updateIndex(); }; select = (): void => { - this.taskPageViewModel.selectBoard(this); + this.taskPageViewModel.selectBoard(this); }; close = (): void => { - this.taskPageViewModel.closeBoard(); - this.taskPageViewModel.storeLastUsedBoard(); - this.taskViewModels.clear(); + this.taskPageViewModel.closeBoard(); + this.taskPageViewModel.storeLastUsedBoard(); + this.taskViewModels.clear(); }; showSettings = (): void => { - this.isPresentingSettingsModal.value = true; + this.isPresentingSettingsModal.value = true; }; hideSettings = (): void => { - this.saveSettings(); - this.isPresentingSettingsModal.value = false; + this.saveSettings(); + this.isPresentingSettingsModal.value = false; }; showFilterModal = (): void => { - this.isPresentingFilterModal.value = true; + this.isPresentingFilterModal.value = true; }; hideFilterModal = (): void => { - this.isPresentingFilterModal.value = false; + this.isPresentingFilterModal.value = false; }; resetFilter = (): void => { - this.searchViewModel.search(""); + this.searchViewModel.search(""); }; updateIndex = (): void => { - const index: number = - this.taskPageViewModel.boardIndexManager.getIndex(this); - this.index.value = index; + const index: number = + this.taskPageViewModel.boardIndexManager.getIndex(this); + this.index.value = index; }; + resetPinchZoom = (): void => { + this.pinchToZoomData.value = {x: 0, y: 0, zoom: 1}; + } + // load preloadData = (): void => { - this.name.value = this.boardInfo.name; - this.color.value = this.boardInfo.color; + this.name.value = this.boardInfo.name; + this.color.value = this.boardInfo.color; }; loadTasks = (): void => { - const taskIds: string[] = this.boardsAndTasksModel.listTaskIds( - this.boardInfo.fileId, - ); - for (const taskId of taskIds) { - if (this.taskViewModels.value.has(taskId)) continue; + const taskIds: string[] = this.boardsAndTasksModel.listTaskIds( + this.boardInfo.fileId, + ); + for (const taskId of taskIds) { + if (this.taskViewModels.value.has(taskId)) continue; - const taskFileContent: TaskFileContent | null = - this.boardsAndTasksModel.getLatestTaskFileContent(taskId); - if (taskFileContent == null) continue; + const taskFileContent: TaskFileContent | null = + this.boardsAndTasksModel.getLatestTaskFileContent(taskId); + if (taskFileContent == null) continue; - const taskViewModel: TaskViewModel = new TaskViewModel( - this.coreViewModel, - this.chatViewModel, - this.boardsAndTasksModel, - this, - taskFileContent, - ); - this.taskViewModels.set(taskFileContent.fileId, taskViewModel); - } + const taskViewModel: TaskViewModel = new TaskViewModel( + this.coreViewModel, + this.chatViewModel, + this.boardsAndTasksModel, + this, + taskFileContent, + ); + this.taskViewModels.set(taskFileContent.fileId, taskViewModel); + } - this.updateTaskIndices(); + this.updateTaskIndices(); }; loadSearchSuggestions = (): void => { - const dirPath: string[] = this.getPreviousSearchesPath(); - const searches: string[] = - this.coreViewModel.storageModel.list(dirPath); - this.searchSuggestions.add(...searches.filter((x) => x != "")); + const dirPath: string[] = this.getPreviousSearchesPath(); + const searches: string[] = + this.coreViewModel.storageModel.list(dirPath); + this.searchSuggestions.add(...searches.filter((x) => x != "")); }; restoreSearch = (): void => { - const lastSearchPath: string[] = this.getLastSearchPath(); - const lastSearch: string | null = - this.coreViewModel.storageModel.read(lastSearchPath); - if (lastSearch != null) { - this.searchViewModel.search(lastSearch); - } + const lastSearchPath: string[] = this.getLastSearchPath(); + const lastSearch: string | null = + this.coreViewModel.storageModel.read(lastSearchPath); + if (lastSearch != null) { + this.searchViewModel.search(lastSearch); + } }; loadData = (): void => { - this.restoreLastUsedView(); - this.loadTasks(); - this.loadSearchSuggestions(); + this.restoreLastUsedView(); + this.loadTasks(); + this.loadSearchSuggestions(); }; // exit handleContextClose = (fromHistoryEvent: boolean): void => { - this.taskPageViewModel.handleBoardClosed(this); + this.taskPageViewModel.handleBoardClosed(this); - if (!fromHistoryEvent) return; - this.taskPageViewModel.storeLastUsedBoard(); + if (!fromHistoryEvent) return; + this.taskPageViewModel.storeLastUsedBoard(); }; // init constructor( - public readonly coreViewModel: CoreViewModel, - public readonly chatViewModel: ChatViewModel, - public readonly boardsAndTasksModel: BoardsAndTasksModel, - public readonly taskPageViewModel: TaskPageViewModel, - public readonly boardInfo: BoardInfoFileContent, + public readonly coreViewModel: CoreViewModel, + public readonly chatViewModel: ChatViewModel, + public readonly boardsAndTasksModel: BoardsAndTasksModel, + public readonly taskPageViewModel: TaskPageViewModel, + public readonly boardInfo: BoardInfoFileContent, ) { - super(coreViewModel, chatViewModel, boardsAndTasksModel, "board"); + super(coreViewModel, chatViewModel, boardsAndTasksModel, "board"); - // load - this.preloadData(); + // load + this.preloadData(); + + // search + this.searchViewModel = new SearchViewModel( + this.taskViewModels, + this.filteredTaskViewModels, + TaskViewModel.getStringsForFilter, + this.searchSuggestions, + ); + this.searchViewModel.appliedQuery.subscribeSilent((newQuery) => { + this.handleNewSearch(newQuery); + }); + this.restoreSearch(); + this.searchSuggestions.handleRemovals(this.handleSearchRemoved); - // subscriptions - this.isSelected = React.createProxyState( - [this.taskPageViewModel.selectedBoardId], - () => - this.taskPageViewModel.selectedBoardId.value == - this.boardInfo.fileId, - ); + this.isFilterActive = React.createProxyState( + [this.searchViewModel.appliedQuery], + () => this.searchViewModel.appliedQuery.value != "", + ); - this.color.subscribe(() => { - if (this.isSelected.value == false) return; - if (this.chatViewModel.selectedPage.value != ChatPageTypes.Tasks) - return; - this.applyColor(); - }); + // subscriptions + this.isSelected = React.createProxyState( + [this.taskPageViewModel.selectedBoardId], + () => + this.taskPageViewModel.selectedBoardId.value == + this.boardInfo.fileId, + ); - this.selectedPage.subscribeSilent(() => { - this.storeLastUsedView(); - }); + this.color.subscribe(() => { + if (this.isSelected.value == false) return; + if (this.chatViewModel.selectedPage.value != ChatPageTypes.Tasks) + return; + this.applyColor(); + }); - // handlers - boardsAndTasksModel.taskHandlerManager.setHandler( - this.boardInfo.fileId, - (taskFileContent: TaskFileContent) => { - if (taskFileContent.boardId != this.boardInfo.fileId) return; - this.showTask(taskFileContent); - this.updateTaskIndices(); - }, - ); + this.selectedPage.subscribeSilent(() => { + this.storeLastUsedView(); + }); - // search - this.searchViewModel = new SearchViewModel( - this.taskViewModels, - this.filteredTaskViewModels, - TaskViewModel.getStringsForFilter, - this.searchSuggestions, - ); - this.searchViewModel.appliedQuery.subscribeSilent((newQuery) => { - this.handleNewSearch(newQuery); - }); - this.restoreSearch(); - this.searchSuggestions.handleRemovals(this.handleSearchRemoved); + this.searchViewModel.appliedQuery.subscribeSilent(()=> { + this.resetPinchZoom(); + }) - this.isFilterActive = React.createProxyState( - [this.searchViewModel.appliedQuery], - () => this.searchViewModel.appliedQuery.value != "", - ); + // handlers + boardsAndTasksModel.taskHandlerManager.setHandler( + this.boardInfo.fileId, + (taskFileContent: TaskFileContent) => { + if (taskFileContent.boardId != this.boardInfo.fileId) return; + this.showTask(taskFileContent); + this.updateTaskIndices(); + }, + ); - // keystrokes - this.registerKeyStroke(CommonKeys.Filter, this.showFilterModal); - this.registerKeyStroke(CommonKeys.Reset, this.resetFilter); // - this.registerKeyStroke(CommonKeys.CloseOrCancel, this.hideFilterModal); - this.registerKeyStroke(CommonKeys.Settings, this.showSettings); - this.registerKeyStroke(CommonKeys.Apply, this.hideSettings); - this.registerKeyStroke(CommonKeys.Create, this.createTask); + // keystrokes + this.registerKeyStroke(CommonKeys.Filter, this.showFilterModal); + this.registerKeyStroke(CommonKeys.Reset, this.resetFilter); // + this.registerKeyStroke(CommonKeys.CloseOrCancel, this.hideFilterModal); + this.registerKeyStroke(CommonKeys.Settings, this.showSettings); + this.registerKeyStroke(CommonKeys.Apply, this.hideSettings); + this.registerKeyStroke(CommonKeys.Create, this.createTask); - this.registerKeyStroke( - "j", - () => (this.selectedPage.value = BoardPageTypes.List), - ); - this.registerKeyStroke( - "k", - () => (this.selectedPage.value = BoardPageTypes.Kanban), - ); - this.registerKeyStroke( - "l", - () => (this.selectedPage.value = BoardPageTypes.StatusGrid), - ); + this.registerKeyStroke( + "j", + () => (this.selectedPage.value = BoardPageTypes.List), + ); + this.registerKeyStroke( + "k", + () => (this.selectedPage.value = BoardPageTypes.Kanban), + ); + this.registerKeyStroke( + "l", + () => (this.selectedPage.value = BoardPageTypes.StatusGrid), + ); - this.taskPageViewModel.registerContext(this.boardInfo.fileId, this); + this.taskPageViewModel.registerContext(this.boardInfo.fileId, this); } } export enum BoardViewModelSubPaths { LastUsedView = "last-used-view", - PreviousSearches = "previous-searches", - LastSearch = "last-search", + PreviousSearches = "previous-searches", + LastSearch = "last-search", } // types export enum BoardPageTypes { List = "list", - Kanban = "kanban", - StatusGrid = "status-grid", + Kanban = "kanban", + StatusGrid = "status-grid", } diff --git a/src/ViewModel/Pages/homeViewModel.ts b/src/ViewModel/Pages/homeViewModel.ts index 720a0277..8f0bfd47 100644 --- a/src/ViewModel/Pages/homeViewModel.ts +++ b/src/ViewModel/Pages/homeViewModel.ts @@ -1,3 +1,5 @@ +import * as React from "../../react"; + import StorageViewModel from "../Global/storageViewModel"; import SettingsViewModel from "../Global/settingsViewModel"; import FileTransferViewModel from "../Global/fileTransferViewModel"; @@ -7,26 +9,26 @@ import { CommonKeys } from "../../View/keystrokes"; export default class HomeViewModel extends Context { constructor( - public readonly coreViewModel: CoreViewModel, - public readonly settingsViewModel: SettingsViewModel, - public readonly fileTransferViewModel: FileTransferViewModel, - public readonly storageViewModel: StorageViewModel, - public readonly connectionViewModel: ConnectionViewModel, + public readonly coreViewModel: CoreViewModel, + public readonly settingsViewModel: SettingsViewModel, + public readonly fileTransferViewModel: FileTransferViewModel, + public readonly storageViewModel: StorageViewModel, + public readonly connectionViewModel: ConnectionViewModel, ) { - super("home"); + super("home"); - this.coreViewModel.context = this; + this.coreViewModel.context = this; - this.registerKeyStroke( - CommonKeys.Settings, - this.settingsViewModel.showSettingsModal, - ); - this.registerKeyStroke( - "t", - this.fileTransferViewModel.showDirectionSelectionModal, - ); - this.registerKeyStroke("e", this.storageViewModel.showStorageModal); - this.registerKeyStroke("x", this.connectionViewModel.disconnect); - this.registerKeyStroke("c", this.connectionViewModel.connect); + this.registerKeyStroke( + CommonKeys.Settings, + this.settingsViewModel.showSettingsModal, + ); + this.registerKeyStroke( + "t", + this.fileTransferViewModel.showDirectionSelectionModal, + ); + this.registerKeyStroke("e", this.storageViewModel.showStorageModal); + this.registerKeyStroke("x", this.connectionViewModel.disconnect); + this.registerKeyStroke("c", this.connectionViewModel.connect); } } diff --git a/src/ViewModel/Utility/searchViewModel.ts b/src/ViewModel/Utility/searchViewModel.ts index a401f8f6..311a961e 100644 --- a/src/ViewModel/Utility/searchViewModel.ts +++ b/src/ViewModel/Utility/searchViewModel.ts @@ -40,6 +40,7 @@ export default class SearchViewModel { clear = (): void => { this.searchInput.value = ""; + this.applySearch(); }; deleteSuggestion = (suggestion: string): void => {