From b1201abedbbe8f290e4d94e6b5e3b5cac088e01d Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 14 Aug 2024 13:30:55 -0400 Subject: [PATCH 01/11] add new UI field for resource slug --- .../jvm/controls/card/TranslationCard2.kt | 13 ++-- .../controls/model/ProjectGroupCardModel.kt | 3 +- .../jvm/controls/model/ProjectGroupKey.kt | 2 +- .../model/WorkbookDescriptorWrapper.kt | 1 + .../main/resources/css/translation-card-2.css | 4 +- .../ui/dev/TranslationCardsDemoView.kt | 78 ------------------- .../jvm/workbookapp/ui/screens/HomePage2.kt | 1 + .../ui/viewmodel/HomePageViewModel2.kt | 3 +- .../src/main/resources/Messages_en.properties | 3 +- 9 files changed, 17 insertions(+), 91 deletions(-) delete mode 100644 jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/dev/TranslationCardsDemoView.kt diff --git a/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/card/TranslationCard2.kt b/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/card/TranslationCard2.kt index 58d76eb801..def09249fc 100644 --- a/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/card/TranslationCard2.kt +++ b/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/card/TranslationCard2.kt @@ -46,11 +46,12 @@ class TranslationCard2( private val sourceLanguage: Language, private val targetLanguage: Language, private val mode: ProjectMode, + private val resourceSlug: String, selectedProjectGroupProperty: ObservableValue ) : ButtonBase() { val cardTitleProperty = SimpleStringProperty( - MessageFormat.format(messages["translationMode"], messages[mode.titleKey]) + MessageFormat.format(messages["projectModeTitle"], resourceSlug.uppercase(), messages[mode.titleKey]) ) val sourceLanguageProperty = SimpleObjectProperty(sourceLanguage) val targetLanguageProperty = SimpleObjectProperty(targetLanguage) @@ -87,7 +88,7 @@ class TranslationCard2( } } - private fun getKey() = ProjectGroupKey(sourceLanguage.slug, targetLanguage.slug, mode) + private fun getKey() = ProjectGroupKey(sourceLanguage.slug, targetLanguage.slug, resourceSlug, mode) } class TranslationCardSkin2(card: TranslationCard2) : SkinBase(card) { @@ -163,10 +164,7 @@ class ActiveTranslationCardSkin(card: TranslationCard2) : SkinBase = SimpleObjectProperty(), op: TranslationCard2.() -> Unit = {} -) = TranslationCard2(sourceLanguage, targetLanguage, mode, selectedCardProperty).attachTo(this, op) +) = TranslationCard2(sourceLanguage, targetLanguage, mode, resourceSlug, selectedCardProperty).attachTo(this, op) diff --git a/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/model/ProjectGroupCardModel.kt b/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/model/ProjectGroupCardModel.kt index b799875e49..e5b2e860be 100644 --- a/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/model/ProjectGroupCardModel.kt +++ b/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/model/ProjectGroupCardModel.kt @@ -27,9 +27,10 @@ class ProjectGroupCardModel( val sourceLanguage: Language, val targetLanguage: Language, val mode: ProjectMode, + val resourceSlug: String, val modifiedTs: LocalDateTime?, val books: ObservableList ) { val booksModel = books.map { it.workbookDescriptor } - fun getKey() = ProjectGroupKey(sourceLanguage.slug, targetLanguage.slug, mode) + fun getKey() = ProjectGroupKey(sourceLanguage.slug, targetLanguage.slug, resourceSlug, mode) } diff --git a/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/model/ProjectGroupKey.kt b/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/model/ProjectGroupKey.kt index 091664d2c8..5f0dd56200 100644 --- a/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/model/ProjectGroupKey.kt +++ b/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/model/ProjectGroupKey.kt @@ -20,4 +20,4 @@ package org.wycliffeassociates.otter.jvm.controls.model import org.wycliffeassociates.otter.common.data.primitives.ProjectMode -data class ProjectGroupKey(val sourceLanguage: String, val targetLanguage: String, val mode: ProjectMode) \ No newline at end of file +data class ProjectGroupKey(val sourceLanguage: String, val targetLanguage: String, val resourceSlug: String, val mode: ProjectMode) \ No newline at end of file diff --git a/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/model/WorkbookDescriptorWrapper.kt b/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/model/WorkbookDescriptorWrapper.kt index fa1f35362c..0d10d6c18a 100644 --- a/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/model/WorkbookDescriptorWrapper.kt +++ b/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/model/WorkbookDescriptorWrapper.kt @@ -23,6 +23,7 @@ data class WorkbookDescriptorWrapper( val lastModified = workbookDescriptor.targetCollection.modifiedTs val sourceLanguage = workbookDescriptor.sourceLanguage val targetLanguage = workbookDescriptor.targetLanguage + val sourceMetadataSlug = workbookDescriptor.sourceCollection.resourceContainer!!.identifier val anthology = workbookDescriptor.anthology val progressProperty = SimpleDoubleProperty(0.0) diff --git a/jvm/controls/src/main/resources/css/translation-card-2.css b/jvm/controls/src/main/resources/css/translation-card-2.css index 060f342031..f75e722126 100644 --- a/jvm/controls/src/main/resources/css/translation-card-2.css +++ b/jvm/controls/src/main/resources/css/translation-card-2.css @@ -18,7 +18,7 @@ */ .translation-card { -fx-padding: 12px 16px; - -fx-spacing: 8px; + -fx-spacing: 12px; -fx-background-radius: 16px; -fx-background-color: -wa-foreground; -fx-background-color: transparent; @@ -65,7 +65,7 @@ } .translation-card__body { - -fx-spacing: 3; + -fx-spacing: 12px; -fx-alignment: center-left; } diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/dev/TranslationCardsDemoView.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/dev/TranslationCardsDemoView.kt deleted file mode 100644 index d36914e254..0000000000 --- a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/dev/TranslationCardsDemoView.kt +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (C) 2020-2024 Wycliffe Associates - * - * This file is part of Orature. - * - * Orature is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Orature is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Orature. If not, see . - */ -package org.wycliffeassociates.otter.jvm.workbookapp.ui.dev - -import javafx.beans.property.SimpleBooleanProperty -import javafx.beans.property.SimpleObjectProperty -import org.wycliffeassociates.otter.common.data.primitives.Language -import org.wycliffeassociates.otter.jvm.controls.card.newTranslationCard -import org.wycliffeassociates.otter.jvm.controls.card.translationCreationCard -import org.wycliffeassociates.otter.common.data.primitives.ProjectMode -import org.wycliffeassociates.otter.jvm.controls.card.translationCard -import org.wycliffeassociates.otter.jvm.controls.styles.tryImportStylesheet -import tornadofx.* - -class TranslationCardsDemoView : View() { - - val languages = listOf( - Language("en", "English", "English", "", true, ""), - Language("fr", "français", "French", "", true, ""), - ) - - init { - tryImportStylesheet("/css/translation-card-2.css") - } - - private val showNewTranslationCard = SimpleBooleanProperty(false) - - override val root = vbox { - spacing = 10.0 - paddingAll = 20.0 - maxWidth = 300.0 - - translationCard( - languages[0], - languages[1], - ProjectMode.TRANSLATION - ) - - newTranslationCard( - SimpleObjectProperty( - languages[0] - ), - SimpleObjectProperty(null), - mode = SimpleObjectProperty() - ) { - visibleWhen(showNewTranslationCard) - managedWhen(visibleProperty()) - - setOnCancelAction { - showNewTranslationCard.set(false) - } - } - translationCreationCard { - visibleWhen(showNewTranslationCard.not()) - managedWhen(visibleProperty()) - - setOnAction { - showNewTranslationCard.set(true) - } - } - } -} \ No newline at end of file diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/HomePage2.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/HomePage2.kt index 49e25f5c3a..e2e3467599 100644 --- a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/HomePage2.kt +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/HomePage2.kt @@ -183,6 +183,7 @@ class HomePage2 : View() { cardModel.sourceLanguage, cardModel.targetLanguage, cardModel.mode, + cardModel.resourceSlug, viewModel.selectedProjectGroupProperty ).apply { diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/HomePageViewModel2.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/HomePageViewModel2.kt index 8fe7ce294c..04820c3e68 100644 --- a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/HomePageViewModel2.kt +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/HomePageViewModel2.kt @@ -346,7 +346,7 @@ class HomePageViewModel2 : ViewModel() { } val projectGroups = books.groupBy { - ProjectGroupKey(it.sourceLanguage.slug, it.targetLanguage.slug, it.mode) + ProjectGroupKey(it.sourceLanguage.slug, it.targetLanguage.slug, it.sourceMetadataSlug, it.mode) } projectGroups .map { entry -> @@ -360,6 +360,7 @@ class HomePageViewModel2 : ViewModel() { book.sourceLanguage, book.targetLanguage, book.mode, + book.sourceMetadataSlug, mostRecentBook?.lastModified, bookList.toObservable() ) diff --git a/jvm/workbookapp/src/main/resources/Messages_en.properties b/jvm/workbookapp/src/main/resources/Messages_en.properties index 46b2c46101..44dada8d4e 100644 --- a/jvm/workbookapp/src/main/resources/Messages_en.properties +++ b/jvm/workbookapp/src/main/resources/Messages_en.properties @@ -381,9 +381,10 @@ display = Display anthology = Anthology translation = Translation narration = Narration -translationMode = Mode: {0} book = Book project = Project +# Example: ULB = {0}, Narration = {1}. Thus, the default ordering for {0} {1} will be "ULB Narration" +projectModeTitle = {0} {1} projects = Projects progress = Progress recording = Recording From 2ae374b83ee51c03e2ae0da05818877a07120b03 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 14 Aug 2024 13:32:29 -0400 Subject: [PATCH 02/11] bold title --- jvm/controls/src/main/resources/css/translation-card-2.css | 1 + 1 file changed, 1 insertion(+) diff --git a/jvm/controls/src/main/resources/css/translation-card-2.css b/jvm/controls/src/main/resources/css/translation-card-2.css index f75e722126..ea53521836 100644 --- a/jvm/controls/src/main/resources/css/translation-card-2.css +++ b/jvm/controls/src/main/resources/css/translation-card-2.css @@ -56,6 +56,7 @@ .translation-card__header .translation-card__header__text { -fx-text-fill: -wa-regular-text; + -fx-font-weight: bold; } .translation-card__header .ikonli-font-icon, From b1da851de307509dec6ea9920665cd6d3f356c96 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 14 Aug 2024 15:00:54 -0400 Subject: [PATCH 03/11] css for subtitle --- .../jvm/controls/card/NewTranslationCard2.kt | 52 ++++++++++--------- .../jvm/controls/card/TranslationCard2.kt | 24 ++++++--- .../main/resources/css/translation-card-2.css | 4 ++ 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/card/NewTranslationCard2.kt b/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/card/NewTranslationCard2.kt index 5ff16f8fe9..f3bd07931e 100644 --- a/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/card/NewTranslationCard2.kt +++ b/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/card/NewTranslationCard2.kt @@ -64,32 +64,36 @@ class NewTranslationCard2( } vbox { addClass("translation-card__body") - label { - addClass("translation-card__language") - textProperty().bind( - sourceLanguageProperty.stringBinding { source -> - togglePseudoClass("unset", source == null) - source?.name ?: "???" - } - ) - graphic = FontIcon(Material.HEARING) - } - label { - addClass("translation-card__divider") - sourceLanguageProperty.onChangeAndDoNow { source -> - togglePseudoClass("unset", source == null) + vbox { + label(messages["sourceLanguage"]) { + addClass("h5", "translation-card__subtitle") + } + label { + addClass("translation-card__language") + textProperty().bind( + sourceLanguageProperty.stringBinding { source -> + togglePseudoClass("unset", source == null) + source?.name ?: "???" + } + ) + graphic = FontIcon(Material.HEARING) } - graphic = FontIcon(MaterialDesign.MDI_CHEVRON_DOUBLE_DOWN) } - label { - addClass("translation-card__language") - textProperty().bind( - targetLanguageProperty.stringBinding { target -> - togglePseudoClass("unset", target == null) - target?.name ?: "???" - } - ) - graphic = FontIcon(MaterialDesign.MDI_VOICE) + separator() + vbox { + label(messages["targetLanguage"]) { + addClass("h5", "translation-card__subtitle") + } + label { + addClass("translation-card__language") + textProperty().bind( + targetLanguageProperty.stringBinding { target -> + togglePseudoClass("unset", target == null) + target?.name ?: "???" + } + ) + graphic = FontIcon(MaterialDesign.MDI_VOICE) + } } } button(messages["cancel"]) { diff --git a/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/card/TranslationCard2.kt b/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/card/TranslationCard2.kt index def09249fc..435fdf868f 100644 --- a/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/card/TranslationCard2.kt +++ b/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/card/TranslationCard2.kt @@ -160,14 +160,24 @@ class ActiveTranslationCardSkin(card: TranslationCard2) : SkinBase Date: Wed, 14 Aug 2024 16:45:40 -0400 Subject: [PATCH 04/11] wip - add source version step in wizard --- .../jvm/controls/event/HomePageEvents.kt | 2 + .../jvm/controls/model/ResourceVersion.kt | 21 ++++++ .../tableview/ResourceVersionTableRow.kt | 44 +++++++++++++ .../tableview/ResourceVersionTableView.kt | 66 +++++++++++++++++++ .../jvm/workbookapp/ui/screens/HomePage2.kt | 14 +++- .../ui/screens/home/ProjectWizardSection.kt | 39 ++++++++++- .../ui/viewmodel/ProjectWizardViewModel.kt | 38 +++++++++-- .../src/main/resources/Messages_en.properties | 8 ++- 8 files changed, 220 insertions(+), 12 deletions(-) create mode 100644 jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/model/ResourceVersion.kt create mode 100644 jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/components/tableview/ResourceVersionTableRow.kt create mode 100644 jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/components/tableview/ResourceVersionTableView.kt diff --git a/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/event/HomePageEvents.kt b/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/event/HomePageEvents.kt index dcd2a40035..dc6c63cfa2 100644 --- a/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/event/HomePageEvents.kt +++ b/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/event/HomePageEvents.kt @@ -21,10 +21,12 @@ package org.wycliffeassociates.otter.jvm.controls.event import org.wycliffeassociates.otter.common.data.primitives.Language import org.wycliffeassociates.otter.common.data.workbook.WorkbookDescriptor import org.wycliffeassociates.otter.common.domain.resourcecontainer.ImportResult +import org.wycliffeassociates.otter.jvm.controls.model.ResourceVersion import tornadofx.FXEvent import java.io.File class LanguageSelectedEvent(val item: Language) : FXEvent() +class ResourceVersionSelectedEvent(val resourceVersion: ResourceVersion): FXEvent() class ProjectImportFinishEvent( val result: ImportResult, diff --git a/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/model/ResourceVersion.kt b/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/model/ResourceVersion.kt new file mode 100644 index 0000000000..5d100510d7 --- /dev/null +++ b/jvm/controls/src/main/kotlin/org/wycliffeassociates/otter/jvm/controls/model/ResourceVersion.kt @@ -0,0 +1,21 @@ +/** + * Copyright (C) 2020-2024 Wycliffe Associates + * + * This file is part of Orature. + * + * Orature is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Orature is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Orature. If not, see . + */ +package org.wycliffeassociates.otter.jvm.controls.model + +data class ResourceVersion(val slug: String, val name: String) \ No newline at end of file diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/components/tableview/ResourceVersionTableRow.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/components/tableview/ResourceVersionTableRow.kt new file mode 100644 index 0000000000..2e775a8ec5 --- /dev/null +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/components/tableview/ResourceVersionTableRow.kt @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2020-2024 Wycliffe Associates + * + * This file is part of Orature. + * + * Orature is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Orature is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Orature. If not, see . + */ +package org.wycliffeassociates.otter.jvm.workbookapp.ui.components.tableview + +import javafx.scene.control.TableRow +import org.wycliffeassociates.otter.jvm.controls.event.ResourceVersionSelectedEvent +import org.wycliffeassociates.otter.jvm.controls.model.ResourceVersion +import tornadofx.* + +class ResourceVersionTableRow : TableRow() { + override fun updateItem(item: ResourceVersion?, empty: Boolean) { + super.updateItem(item, empty) + + if (item == null || empty) { + isMouseTransparent = true + return + } + + isMouseTransparent = isDisable + isFocusTraversable = false + + setOnMouseClicked { + if (it.clickCount == 1) { // avoid double fire() + FX.eventbus.fire(ResourceVersionSelectedEvent(item)) + } + } + } +} \ No newline at end of file diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/components/tableview/ResourceVersionTableView.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/components/tableview/ResourceVersionTableView.kt new file mode 100644 index 0000000000..3476e249a8 --- /dev/null +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/components/tableview/ResourceVersionTableView.kt @@ -0,0 +1,66 @@ +/** + * Copyright (C) 2020-2024 Wycliffe Associates + * + * This file is part of Orature. + * + * Orature is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Orature is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Orature. If not, see . + */ +package org.wycliffeassociates.otter.jvm.workbookapp.ui.components.tableview + +import javafx.collections.ObservableList +import javafx.event.EventTarget +import javafx.scene.control.TableView +import javafx.scene.layout.Priority +import javafx.scene.layout.Region +import org.wycliffeassociates.otter.jvm.controls.model.ResourceVersion +import tornadofx.* +import tornadofx.FX.Companion.messages + +class ResourceVersionTableView( + resources: ObservableList +): TableView(resources) { + init { + addClass("wa-table-view") + vgrow = Priority.ALWAYS + columnResizePolicy = CONSTRAINED_RESIZE_POLICY_FLEX_LAST_COLUMN + placeholder = Region() // shows nothing when table is empty + + column(messages["resource_name"], String::class) { + addClass("table-view__column-header-row") + setCellValueFactory { it.value.name.toProperty() } + cellFormat { + graphic = label(item) { + addClass("h4", "h4--80") + tooltip(item) + } + } + } + column(messages["code"], String::class).apply { + addClass("table-view__column-header-row") + setCellValueFactory { it.value.slug.toProperty() } + cellFormat { + graphic = label(item) { addClass("normal-text") } + } + } + + setRowFactory { ResourceVersionTableRow() } + + //TODO: accessibility support + } +} + +fun EventTarget.resourceVersionTableView( + values: ObservableList, + op: ResourceVersionTableView.() -> Unit = {} +) = ResourceVersionTableView(values).attachTo(this, op) \ No newline at end of file diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/HomePage2.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/HomePage2.kt index e2e3467599..401da42c16 100644 --- a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/HomePage2.kt +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/HomePage2.kt @@ -58,6 +58,7 @@ import org.wycliffeassociates.otter.jvm.controls.event.ProjectImportEvent import org.wycliffeassociates.otter.jvm.utils.ListenerDisposer import org.wycliffeassociates.otter.jvm.utils.onChangeWithDisposer import org.wycliffeassociates.otter.jvm.controls.event.ProjectContributorsEvent +import org.wycliffeassociates.otter.jvm.controls.event.ResourceVersionSelectedEvent import org.wycliffeassociates.otter.jvm.controls.model.ProjectGroupCardModel import org.wycliffeassociates.otter.jvm.controls.model.ProjectGroupKey import org.wycliffeassociates.otter.jvm.workbookapp.ui.screens.home.BookSection @@ -106,8 +107,10 @@ class HomePage2 : View() { ProjectWizardSection( projectWizardViewModel.sortedSourceLanguages, projectWizardViewModel.sortedTargetLanguages, + projectWizardViewModel.resourceVersions, projectWizardViewModel.selectedModeProperty, projectWizardViewModel.selectedSourceLanguageProperty, + projectWizardViewModel.selectedTargetLanguageProperty, projectWizardViewModel.existingLanguagePairs ).apply { @@ -239,12 +242,12 @@ class HomePage2 : View() { val projectMode = projectWizardViewModel.selectedModeProperty.value if (selectedSource == null && projectMode != ProjectMode.NARRATION) { - wizardFragment.nextStep() } + wizardFragment.nextStep() if (selectedSource != null || projectMode == ProjectMode.NARRATION) { // open loading dialog when creating project - viewModel.isLoadingProperty.set(true) +// viewModel.isLoadingProperty.set(true) } projectWizardViewModel.onLanguageSelected(projectMode, it.item) { @@ -253,6 +256,13 @@ class HomePage2 : View() { } } + subscribe { + projectWizardViewModel.onResourceVersionSelected(it.resourceVersion) { + viewModel.loadProjects() + mainSectionProperty.set(bookFragment) + } + } + subscribe { val books = it.books val dialog = find().apply { diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/home/ProjectWizardSection.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/home/ProjectWizardSection.kt index bde10144ad..4bcaf9b21c 100644 --- a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/home/ProjectWizardSection.kt +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/home/ProjectWizardSection.kt @@ -38,9 +38,11 @@ import org.wycliffeassociates.otter.common.data.primitives.ProjectMode import org.wycliffeassociates.otter.jvm.controls.bar.searchBar import org.wycliffeassociates.otter.jvm.controls.card.translationTypeCard import org.wycliffeassociates.otter.jvm.controls.customizeScrollbarSkin +import org.wycliffeassociates.otter.jvm.controls.model.ResourceVersion import org.wycliffeassociates.otter.jvm.controls.model.StepDirection import org.wycliffeassociates.otter.jvm.utils.onChangeAndDoNow import org.wycliffeassociates.otter.jvm.workbookapp.ui.components.tableview.languageTableView +import org.wycliffeassociates.otter.jvm.workbookapp.ui.components.tableview.resourceVersionTableView import tornadofx.* import tornadofx.FX.Companion.messages @@ -49,8 +51,10 @@ private const val TRANSITION_DURATION_SEC = 0.3 class ProjectWizardSection( sourceLanguages: ObservableList, targetLanguages: ObservableList, + resourceVersions: ObservableList, selectedModeProperty: SimpleObjectProperty, selectedSourceLanguageProperty: SimpleObjectProperty, + selectedTargetLanguageProperty: SimpleObjectProperty, existingLanguagePairs: ObservableList> ) : StackPane() { val sourceLanguageSearchQueryProperty = SimpleStringProperty() @@ -190,6 +194,37 @@ class ProjectWizardSection( managedWhen(visibleProperty()) } + private val step4 = VBox().apply { + addClass("homepage__main-region") + + hbox { + addClass("homepage__main-region__header-section") + + button { + addClass("btn", "btn--icon") + tooltip(messages["goBack"]) + graphic = FontIcon(MaterialDesign.MDI_ARROW_LEFT) + + setOnAction { + selectedTargetLanguageProperty.set(null) + previousStep() + } + } + label(messages["selectSourceVersionStep4"]) { addClass("h4") } + region { hgrow = Priority.ALWAYS } +// searchBar { +// textProperty().bindBidirectional() +// promptText = messages["search"] +// } + } + resourceVersionTableView(resourceVersions) { + this@apply.visibleProperty().onChange { + if (it) customizeScrollbarSkin() + } + } + managedWhen(visibleProperty()) + } + init { vgrow = Priority.ALWAYS @@ -197,8 +232,9 @@ class ProjectWizardSection( add(step1) add(step2) add(step3) + add(step4) - steps = listOf(step1, step2, step3) + steps = listOf(step1, step2, step3, step4) } fun setOnCancelAction(op: () -> Unit) { @@ -213,6 +249,7 @@ class ProjectWizardSection( runLater { step2.translateX = scene.width step3.translateX = scene.width + step4.translateX = scene.width } } diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt index a4e44e0c0a..d85077e1d5 100644 --- a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt @@ -35,6 +35,7 @@ import org.wycliffeassociates.otter.common.domain.collections.DeleteProject import org.wycliffeassociates.otter.common.domain.project.ImportProjectUseCase import org.wycliffeassociates.otter.common.persistence.repositories.ICollectionRepository import org.wycliffeassociates.otter.common.persistence.repositories.ILanguageRepository +import org.wycliffeassociates.otter.jvm.controls.model.ResourceVersion import org.wycliffeassociates.otter.jvm.utils.ListenerDisposer import org.wycliffeassociates.otter.jvm.utils.onChangeWithDisposer import org.wycliffeassociates.otter.jvm.workbookapp.di.IDependencyGraphProvider @@ -65,10 +66,12 @@ class ProjectWizardViewModel : ViewModel() { val sortedSourceLanguages = SortedList(filteredSourceLanguages) val sortedTargetLanguages = SortedList(filteredTargetLanguage) val existingLanguagePairs = observableListOf>() + val resourceVersions = observableListOf() val selectedModeProperty = SimpleObjectProperty(null) val selectedSourceLanguageProperty = SimpleObjectProperty(null) val selectedTargetLanguageProperty = SimpleObjectProperty(null) + val selectedVersionProperty = SimpleObjectProperty(null) val sourceLanguageSearchQueryProperty = SimpleStringProperty("") val targetLanguageSearchQueryProperty = SimpleStringProperty("") @@ -144,20 +147,43 @@ class ProjectWizardViewModel : ViewModel() { fun onLanguageSelected(projectMode: ProjectMode, language: Language, onNavigateBack: () -> Unit) { val sourceLanguage = selectedSourceLanguageProperty.value + if (sourceLanguage == null) { + resourceVersions.setAll(getAvailableResources(language)) + } + + val ignoreVersionSelect = resourceVersions.size == 1 + val createNarrationProject = ignoreVersionSelect && projectMode == ProjectMode.NARRATION + val createOtherProject = ignoreVersionSelect && sourceLanguage != null - val createNarrationProject = projectMode == ProjectMode.NARRATION - val createOtherProject = projectMode != ProjectMode.NARRATION && sourceLanguage != null - when { - createNarrationProject -> createProject(language, language, onNavigateBack) - createOtherProject -> createProject(sourceLanguage, language, onNavigateBack) - else -> selectedSourceLanguageProperty.set(language) + //TODO: handle quick creation of projects that have only 1 version +// createNarrationProject -> createProject(language, language, onNavigateBack) +// createOtherProject -> createProject(sourceLanguage, language, onNavigateBack) + createNarrationProject -> println("Create narration") + createOtherProject -> println("Create translation/dialect") + sourceLanguage == null -> selectedSourceLanguageProperty.set(language) + else -> selectedTargetLanguageProperty.set(language) } } + fun onResourceVersionSelected(version: ResourceVersion, onNavigateBack: () -> Unit) { + createProject( + selectedSourceLanguageProperty.value, + selectedTargetLanguageProperty.value, + version, + onNavigateBack + ) + } + + private fun getAvailableResources(language: Language): List { + val rootSources = collectionRepo.getRootSources().blockingGet().filter { it.resourceContainer!!.language == language } + return rootSources.map { ResourceVersion(it.slug, it.resourceContainer!!.title) } + } + private fun createProject( sourceLanguage: Language, targetLanguage: Language, + resourceVersion: ResourceVersion, // TODO: use source version when creating project onNavigateBack: () -> Unit ) { logger.info("Creating project group: ${sourceLanguage.name} - ${targetLanguage.name}") diff --git a/jvm/workbookapp/src/main/resources/Messages_en.properties b/jvm/workbookapp/src/main/resources/Messages_en.properties index 44dada8d4e..a551384000 100644 --- a/jvm/workbookapp/src/main/resources/Messages_en.properties +++ b/jvm/workbookapp/src/main/resources/Messages_en.properties @@ -122,13 +122,15 @@ targetLanguage = Target Language sourceLanguage = Source Language pickSourceLanguage = Select a Source Language pickTargetLanguage = Select a Target Language -selectProjectTypeStep1 = Pick a Project Type (1 of 3) -selectSourceLanguageStep2 = Pick a Source Language (2 of 3) -selectTargetLanguageStep3 = Pick a Target Language (3 of 3) +selectProjectTypeStep1 = Pick a Project Type (1 of 4) +selectSourceLanguageStep2 = Pick a Source Language (2 of 4) +selectTargetLanguageStep3 = Pick a Target Language (3 of 4) +selectSourceVersionStep4 = Pick a Version (4 of 4) anglicized = Anglicized code = Code gateway = Gateway region = Region +resource_name = Resource Name search = Search sortBy = Sort by selectBook = Select a Book From a1324de9cdd9189c245cc3ff53be0bd6175b09d1 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 15 Aug 2024 10:36:52 -0400 Subject: [PATCH 05/11] wip create project with resource slug --- .../domain/collections/CreateProject.kt | 8 ++-- .../jvm/workbookapp/ui/screens/HomePage2.kt | 10 ++-- .../ui/viewmodel/ProjectWizardViewModel.kt | 47 ++++++++++++++----- 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/common/src/main/kotlin/org/wycliffeassociates/otter/common/domain/collections/CreateProject.kt b/common/src/main/kotlin/org/wycliffeassociates/otter/common/domain/collections/CreateProject.kt index d182f5f408..2f9c011feb 100644 --- a/common/src/main/kotlin/org/wycliffeassociates/otter/common/domain/collections/CreateProject.kt +++ b/common/src/main/kotlin/org/wycliffeassociates/otter/common/domain/collections/CreateProject.kt @@ -88,15 +88,17 @@ class CreateProject @Inject constructor( fun createAllBooks( sourceLanguage: Language, targetLanguage: Language, - projectMode: ProjectMode + projectMode: ProjectMode, + resourceId: String? = null ): Completable { val isVerseByVerse = projectMode != ProjectMode.TRANSLATION return collectionRepo.getRootSources() .flattenAsObservable { it } - .filter { - it.resourceContainer?.language == sourceLanguage + .filter { collection -> + collection.resourceContainer?.language == sourceLanguage && + (resourceId?.let { collection.resourceContainer?.identifier == resourceId } ?: true) } .firstOrError() .flatMap { rootCollection -> diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/HomePage2.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/HomePage2.kt index 401da42c16..d8d6963171 100644 --- a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/HomePage2.kt +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/HomePage2.kt @@ -141,6 +141,7 @@ class HomePage2 : View() { tryImportStylesheet("/css/snack-bar-notification.css") subscribeActionEvents() + projectWizardViewModel.isLoadingProperty.bindBidirectional(viewModel.isLoadingProperty) } override val root = borderpane { @@ -241,13 +242,8 @@ class HomePage2 : View() { val selectedSource = projectWizardViewModel.selectedSourceLanguageProperty.value val projectMode = projectWizardViewModel.selectedModeProperty.value - if (selectedSource == null && projectMode != ProjectMode.NARRATION) { - } - wizardFragment.nextStep() - - if (selectedSource != null || projectMode == ProjectMode.NARRATION) { - // open loading dialog when creating project -// viewModel.isLoadingProperty.set(true) + if (!projectWizardViewModel.shouldBypassNextSteps()) { + wizardFragment.nextStep() } projectWizardViewModel.onLanguageSelected(projectMode, it.item) { diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt index d85077e1d5..dc92758528 100644 --- a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt @@ -22,6 +22,7 @@ import com.github.thomasnield.rxkotlinfx.observeOnFx import io.reactivex.Completable import io.reactivex.Observable import io.reactivex.schedulers.Schedulers +import javafx.beans.property.SimpleBooleanProperty import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleStringProperty import javafx.beans.value.ObservableValue @@ -75,8 +76,9 @@ class ProjectWizardViewModel : ViewModel() { val sourceLanguageSearchQueryProperty = SimpleStringProperty("") val targetLanguageSearchQueryProperty = SimpleStringProperty("") - private val projectDeleteCounter = AtomicInteger(0) + val isLoadingProperty = SimpleBooleanProperty(false) + private val projectDeleteCounter = AtomicInteger(0) private val disposableListeners = mutableListOf() init { @@ -156,13 +158,21 @@ class ProjectWizardViewModel : ViewModel() { val createOtherProject = ignoreVersionSelect && sourceLanguage != null when { - //TODO: handle quick creation of projects that have only 1 version -// createNarrationProject -> createProject(language, language, onNavigateBack) -// createOtherProject -> createProject(sourceLanguage, language, onNavigateBack) - createNarrationProject -> println("Create narration") - createOtherProject -> println("Create translation/dialect") - sourceLanguage == null -> selectedSourceLanguageProperty.set(language) - else -> selectedTargetLanguageProperty.set(language) + createNarrationProject -> { + createProject(language, language, resourceVersion = null, onNavigateBack) + } + + createOtherProject -> { + createProject(sourceLanguage, language, resourceVersion = null, onNavigateBack) + } + + sourceLanguage == null -> { + selectedSourceLanguageProperty.set(language) + } + + else -> { + selectedTargetLanguageProperty.set(language) + } } } @@ -175,18 +185,32 @@ class ProjectWizardViewModel : ViewModel() { ) } + fun shouldBypassNextSteps(): Boolean { + val selectedMode = selectedModeProperty.value + val selectedSource = selectedSourceLanguageProperty.value + val ignoreVersion = resourceVersions.size == 1 + + return (selectedMode == ProjectMode.NARRATION && selectedSource == null && ignoreVersion) || + (ignoreVersion && selectedSource != null) + } + private fun getAvailableResources(language: Language): List { - val rootSources = collectionRepo.getRootSources().blockingGet().filter { it.resourceContainer!!.language == language } + var rootSources = collectionRepo.getRootSources().blockingGet().filter { it.resourceContainer!!.language == language } + if (rootSources.isEmpty()) { + importer.sideloadSource(language).blockingAwait() + rootSources = collectionRepo.getRootSources().blockingGet().filter { it.resourceContainer!!.language == language } + } return rootSources.map { ResourceVersion(it.slug, it.resourceContainer!!.title) } } private fun createProject( sourceLanguage: Language, targetLanguage: Language, - resourceVersion: ResourceVersion, // TODO: use source version when creating project + resourceVersion: ResourceVersion?, onNavigateBack: () -> Unit ) { logger.info("Creating project group: ${sourceLanguage.name} - ${targetLanguage.name}") + isLoadingProperty.set(true) // check if source metadata exists for the requested language val sourceExists = collectionRepo.getRootSources().blockingGet() @@ -203,7 +227,8 @@ class ProjectWizardViewModel : ViewModel() { .createAllBooks( sourceLanguage, targetLanguage, - selectedModeProperty.value + selectedModeProperty.value, + resourceVersion?.slug ) .startWith(prepareSource) // must run after deletion and before creation .startWith(waitForProjectDeletionFinishes()) // this must run first From 478b3d44bef1ca952a58b5073341fe42593c86f7 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 15 Aug 2024 12:42:26 -0400 Subject: [PATCH 06/11] side load source when selected before moving to step 2 --- .../ui/viewmodel/ProjectWizardViewModel.kt | 90 +++++++++++++------ 1 file changed, 61 insertions(+), 29 deletions(-) diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt index dc92758528..5350ac5401 100644 --- a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt @@ -21,6 +21,7 @@ package org.wycliffeassociates.otter.jvm.workbookapp.ui.viewmodel import com.github.thomasnield.rxkotlinfx.observeOnFx import io.reactivex.Completable import io.reactivex.Observable +import io.reactivex.Single import io.reactivex.schedulers.Schedulers import javafx.beans.property.SimpleBooleanProperty import javafx.beans.property.SimpleObjectProperty @@ -36,6 +37,7 @@ import org.wycliffeassociates.otter.common.domain.collections.DeleteProject import org.wycliffeassociates.otter.common.domain.project.ImportProjectUseCase import org.wycliffeassociates.otter.common.persistence.repositories.ICollectionRepository import org.wycliffeassociates.otter.common.persistence.repositories.ILanguageRepository +import org.wycliffeassociates.otter.common.persistence.repositories.IResourceMetadataRepository import org.wycliffeassociates.otter.jvm.controls.model.ResourceVersion import org.wycliffeassociates.otter.jvm.utils.ListenerDisposer import org.wycliffeassociates.otter.jvm.utils.onChangeWithDisposer @@ -58,6 +60,8 @@ class ProjectWizardViewModel : ViewModel() { @Inject lateinit var collectionRepo: ICollectionRepository @Inject + lateinit var resourceMetadataRepo: IResourceMetadataRepository + @Inject lateinit var importer: ImportProjectUseCase private val sourceLanguages = observableListOf() @@ -148,32 +152,42 @@ class ProjectWizardViewModel : ViewModel() { } fun onLanguageSelected(projectMode: ProjectMode, language: Language, onNavigateBack: () -> Unit) { + isLoadingProperty.set(true) + val sourceLanguage = selectedSourceLanguageProperty.value - if (sourceLanguage == null) { - resourceVersions.setAll(getAvailableResources(language)) + val availableVersions = if (sourceLanguage == null) { + resourceVersions.clear() + getAvailableResources(language) + } else { + Single.just(resourceVersions) } - val ignoreVersionSelect = resourceVersions.size == 1 - val createNarrationProject = ignoreVersionSelect && projectMode == ProjectMode.NARRATION - val createOtherProject = ignoreVersionSelect && sourceLanguage != null - - when { - createNarrationProject -> { - createProject(language, language, resourceVersion = null, onNavigateBack) - } - - createOtherProject -> { - createProject(sourceLanguage, language, resourceVersion = null, onNavigateBack) - } - - sourceLanguage == null -> { - selectedSourceLanguageProperty.set(language) - } - - else -> { - selectedTargetLanguageProperty.set(language) + availableVersions + .subscribe { versions -> + val ignoreVersionSelect = versions.size == 1 + val createNarrationProject = ignoreVersionSelect && projectMode == ProjectMode.NARRATION + val createOtherProject = ignoreVersionSelect && sourceLanguage != null + + when { + createNarrationProject -> { + createProject(language, language, resourceVersion = null, onNavigateBack) + } + + createOtherProject -> { + createProject(sourceLanguage, language, resourceVersion = null, onNavigateBack) + } + + sourceLanguage == null -> { + isLoadingProperty.set(false) + selectedSourceLanguageProperty.set(language) + } + + else -> { + isLoadingProperty.set(false) + selectedTargetLanguageProperty.set(language) + } + } } - } } fun onResourceVersionSelected(version: ResourceVersion, onNavigateBack: () -> Unit) { @@ -194,13 +208,27 @@ class ProjectWizardViewModel : ViewModel() { (ignoreVersion && selectedSource != null) } - private fun getAvailableResources(language: Language): List { - var rootSources = collectionRepo.getRootSources().blockingGet().filter { it.resourceContainer!!.language == language } - if (rootSources.isEmpty()) { - importer.sideloadSource(language).blockingAwait() - rootSources = collectionRepo.getRootSources().blockingGet().filter { it.resourceContainer!!.language == language } - } - return rootSources.map { ResourceVersion(it.slug, it.resourceContainer!!.title) } + private fun getAvailableResources(language: Language): Single> { + return resourceMetadataRepo + .exists { it.language == language } + .flatMapCompletable { exists -> + if (!exists) { + importer.sideloadSource(language) + } else { + Completable.complete() + } + } + .andThen( + resourceMetadataRepo.getAllSources() + ) + .map { resources -> + resources + .filter { it.language == language } + .map { ResourceVersion(it.identifier, it.title)} + } + .subscribeOn(Schedulers.io()) + .observeOnFx() + .doOnSuccess { resourceVersions.setAll(it) } } private fun createProject( @@ -249,6 +277,7 @@ class ProjectWizardViewModel : ViewModel() { fun dock() { updateExistingLanguagePairs() + reset() selectedModeProperty.onChangeWithDisposer { loadSourceLanguages() @@ -293,6 +322,9 @@ class ProjectWizardViewModel : ViewModel() { selectedTargetLanguageProperty.set(null) sourceLanguageSearchQueryProperty.set("") targetLanguageSearchQueryProperty.set("") + sourceLanguages.clear() + targetLanguages.clear() + resourceVersions.clear() } private fun updateExistingLanguagePairs() { From ff120e6a4610a5de35310ee5e23028bca4e645f3 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 15 Aug 2024 14:55:23 -0400 Subject: [PATCH 07/11] handle creating duplicate project --- .../jvm/workbookapp/ui/screens/HomePage2.kt | 4 +- .../ui/screens/home/ProjectWizardSection.kt | 29 +-------- .../ui/viewmodel/HomePageViewModel2.kt | 11 ++-- .../ui/viewmodel/ProjectWizardViewModel.kt | 61 ++++++++++++++----- 4 files changed, 56 insertions(+), 49 deletions(-) diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/HomePage2.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/HomePage2.kt index d8d6963171..b2d36849b5 100644 --- a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/HomePage2.kt +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/HomePage2.kt @@ -110,8 +110,7 @@ class HomePage2 : View() { projectWizardViewModel.resourceVersions, projectWizardViewModel.selectedModeProperty, projectWizardViewModel.selectedSourceLanguageProperty, - projectWizardViewModel.selectedTargetLanguageProperty, - projectWizardViewModel.existingLanguagePairs + projectWizardViewModel.selectedTargetLanguageProperty ).apply { sourceLanguageSearchQueryProperty.bindBidirectional(projectWizardViewModel.sourceLanguageSearchQueryProperty) @@ -142,6 +141,7 @@ class HomePage2 : View() { subscribeActionEvents() projectWizardViewModel.isLoadingProperty.bindBidirectional(viewModel.isLoadingProperty) + projectWizardViewModel.bookMarkedProjectGroupProperty.bindBidirectional(viewModel.bookMarkedProjectGroupProperty) } override val root = borderpane { diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/home/ProjectWizardSection.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/home/ProjectWizardSection.kt index 4bcaf9b21c..0a5f734ef9 100644 --- a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/home/ProjectWizardSection.kt +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/screens/home/ProjectWizardSection.kt @@ -19,7 +19,6 @@ package org.wycliffeassociates.otter.jvm.workbookapp.ui.screens.home import javafx.animation.TranslateTransition -import javafx.beans.binding.Bindings import javafx.beans.property.SimpleIntegerProperty import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleStringProperty @@ -40,7 +39,6 @@ import org.wycliffeassociates.otter.jvm.controls.card.translationTypeCard import org.wycliffeassociates.otter.jvm.controls.customizeScrollbarSkin import org.wycliffeassociates.otter.jvm.controls.model.ResourceVersion import org.wycliffeassociates.otter.jvm.controls.model.StepDirection -import org.wycliffeassociates.otter.jvm.utils.onChangeAndDoNow import org.wycliffeassociates.otter.jvm.workbookapp.ui.components.tableview.languageTableView import org.wycliffeassociates.otter.jvm.workbookapp.ui.components.tableview.resourceVersionTableView import tornadofx.* @@ -55,7 +53,6 @@ class ProjectWizardSection( selectedModeProperty: SimpleObjectProperty, selectedSourceLanguageProperty: SimpleObjectProperty, selectedTargetLanguageProperty: SimpleObjectProperty, - existingLanguagePairs: ObservableList> ) : StackPane() { val sourceLanguageSearchQueryProperty = SimpleStringProperty() val targetLanguageSearchQueryProperty = SimpleStringProperty() @@ -131,18 +128,6 @@ class ProjectWizardSection( } languageTableView(sourceLanguages) { - selectedModeProperty.onChange { - if (it == ProjectMode.NARRATION) { - val duplicated = existingLanguagePairs - .filter { it.first == it.second } - .map { it.first } - - disabledLanguages.setAll(duplicated) - } else { - disabledLanguages.clear() - } - } - this@apply.visibleProperty().onChange { if (it) customizeScrollbarSkin() } @@ -175,18 +160,6 @@ class ProjectWizardSection( } languageTableView(targetLanguages) { - selectedSourceLanguageProperty.onChange { - it?.let { src -> - val duplicated = existingLanguagePairs - .filter { it.first == src } - .map { it.second } - - disabledLanguages.setAll(duplicated) - } ?: { - disabledLanguages.clear() - } - } - this@apply.visibleProperty().onChange { if (it) customizeScrollbarSkin() } @@ -212,6 +185,8 @@ class ProjectWizardSection( } label(messages["selectSourceVersionStep4"]) { addClass("h4") } region { hgrow = Priority.ALWAYS } + + // TODO: add search bar // searchBar { // textProperty().bindBidirectional() // promptText = messages["search"] diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/HomePageViewModel2.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/HomePageViewModel2.kt index 04820c3e68..482be7e692 100644 --- a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/HomePageViewModel2.kt +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/HomePageViewModel2.kt @@ -90,6 +90,7 @@ class HomePageViewModel2 : ViewModel() { val sortedBooks = SortedList(filteredBooks) val selectedProjectGroupProperty = SimpleObjectProperty() + val bookMarkedProjectGroupProperty = SimpleObjectProperty() val bookSearchQueryProperty = SimpleStringProperty("") val isLoadingProperty = SimpleBooleanProperty(false) @@ -209,9 +210,9 @@ class HomePageViewModel2 : ViewModel() { .observeOnFx() .doOnComplete { logger.info("Deleted project group: ${cardModel.sourceLanguage.name} -> ${cardModel.targetLanguage.name}.") - projectWizardViewModel.existingLanguagePairs.remove( - Pair(cardModel.sourceLanguage, cardModel.targetLanguage) - ) +// projectWizardViewModel.existingLanguagePairs.remove( +// Pair(cardModel.sourceLanguage, cardModel.targetLanguage) +// ) } .doOnDispose { logger.info("Cancelled deleting project group ${cardModel.sourceLanguage.name} -> ${cardModel.targetLanguage.name}.") @@ -369,7 +370,9 @@ class HomePageViewModel2 : ViewModel() { .let { modelList -> this.projectGroups.setAll(modelList) modelList.firstOrNull()?.let { cardModel -> - selectedProjectGroupProperty.set(cardModel.getKey()) + val selectedProject = bookMarkedProjectGroupProperty.value ?: cardModel.getKey() + selectedProjectGroupProperty.set(selectedProject) + bookMarkedProjectGroupProperty.set(null) bookList.setAll(cardModel.books) } } diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt index 5350ac5401..cbb4558f74 100644 --- a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt @@ -38,7 +38,10 @@ import org.wycliffeassociates.otter.common.domain.project.ImportProjectUseCase import org.wycliffeassociates.otter.common.persistence.repositories.ICollectionRepository import org.wycliffeassociates.otter.common.persistence.repositories.ILanguageRepository import org.wycliffeassociates.otter.common.persistence.repositories.IResourceMetadataRepository +import org.wycliffeassociates.otter.common.persistence.repositories.IWorkbookDescriptorRepository +import org.wycliffeassociates.otter.jvm.controls.model.ProjectGroupKey import org.wycliffeassociates.otter.jvm.controls.model.ResourceVersion +import org.wycliffeassociates.otter.jvm.controls.model.WorkbookDescriptorWrapper import org.wycliffeassociates.otter.jvm.utils.ListenerDisposer import org.wycliffeassociates.otter.jvm.utils.onChangeWithDisposer import org.wycliffeassociates.otter.jvm.workbookapp.di.IDependencyGraphProvider @@ -62,6 +65,8 @@ class ProjectWizardViewModel : ViewModel() { @Inject lateinit var resourceMetadataRepo: IResourceMetadataRepository @Inject + lateinit var workbookDescriptorRepo: IWorkbookDescriptorRepository + @Inject lateinit var importer: ImportProjectUseCase private val sourceLanguages = observableListOf() @@ -70,10 +75,10 @@ class ProjectWizardViewModel : ViewModel() { private val filteredTargetLanguage = FilteredList(targetLanguages) val sortedSourceLanguages = SortedList(filteredSourceLanguages) val sortedTargetLanguages = SortedList(filteredTargetLanguage) - val existingLanguagePairs = observableListOf>() val resourceVersions = observableListOf() val selectedModeProperty = SimpleObjectProperty(null) + val selectedMode by selectedModeProperty val selectedSourceLanguageProperty = SimpleObjectProperty(null) val selectedTargetLanguageProperty = SimpleObjectProperty(null) val selectedVersionProperty = SimpleObjectProperty(null) @@ -81,6 +86,7 @@ class ProjectWizardViewModel : ViewModel() { val sourceLanguageSearchQueryProperty = SimpleStringProperty("") val targetLanguageSearchQueryProperty = SimpleStringProperty("") val isLoadingProperty = SimpleBooleanProperty(false) + val bookMarkedProjectGroupProperty = SimpleObjectProperty() private val projectDeleteCounter = AtomicInteger(0) private val disposableListeners = mutableListOf() @@ -135,7 +141,6 @@ class ProjectWizardViewModel : ViewModel() { } .subscribe { languages -> sourceLanguages.setAll(languages) - updateExistingLanguagePairs() } } @@ -157,7 +162,7 @@ class ProjectWizardViewModel : ViewModel() { val sourceLanguage = selectedSourceLanguageProperty.value val availableVersions = if (sourceLanguage == null) { resourceVersions.clear() - getAvailableResources(language) + getResourceVersions(language) } else { Single.just(resourceVersions) } @@ -208,7 +213,7 @@ class ProjectWizardViewModel : ViewModel() { (ignoreVersion && selectedSource != null) } - private fun getAvailableResources(language: Language): Single> { + private fun getResourceVersions(language: Language): Single> { return resourceMetadataRepo .exists { it.language == language } .flatMapCompletable { exists -> @@ -240,6 +245,21 @@ class ProjectWizardViewModel : ViewModel() { logger.info("Creating project group: ${sourceLanguage.name} - ${targetLanguage.name}") isLoadingProperty.set(true) + val existingBook = fetchExistingWorkBook(resourceVersion, sourceLanguage, targetLanguage) + // if the project is already created, bookmark it to open after home page finishes loading + if (existingBook != null) { + bookMarkedProjectGroupProperty.set( + ProjectGroupKey( + existingBook.sourceLanguage.slug, + existingBook.targetLanguage.slug, + existingBook.sourceMetadataSlug, selectedMode + ) + ) + isLoadingProperty.set(false) + onNavigateBack() + return + } + // check if source metadata exists for the requested language val sourceExists = collectionRepo.getRootSources().blockingGet() .any { it.resourceContainer!!.language == sourceLanguage } @@ -255,7 +275,7 @@ class ProjectWizardViewModel : ViewModel() { .createAllBooks( sourceLanguage, targetLanguage, - selectedModeProperty.value, + selectedMode, resourceVersion?.slug ) .startWith(prepareSource) // must run after deletion and before creation @@ -264,19 +284,17 @@ class ProjectWizardViewModel : ViewModel() { .subscribe( { logger.info("Project group created: ${sourceLanguage.name} - ${targetLanguage.name}") - existingLanguagePairs.add(Pair(sourceLanguage, targetLanguage)) reset() onNavigateBack() }, { logger.error("Could not create project for ${sourceLanguage.name} - ${targetLanguage.slug} ${selectedModeProperty.value}") - find().isLoadingProperty.set(false) + isLoadingProperty.set(false) } ) } fun dock() { - updateExistingLanguagePairs() reset() selectedModeProperty.onChangeWithDisposer { @@ -305,6 +323,25 @@ class ProjectWizardViewModel : ViewModel() { fun increaseProjectDeleteCounter() { projectDeleteCounter.incrementAndGet() } fun decreaseProjectDeleteCounter() { projectDeleteCounter.decrementAndGet() } + private fun fetchExistingWorkBook( + resourceVersion: ResourceVersion?, + sourceLanguage: Language, + targetLanguage: Language + ): WorkbookDescriptorWrapper? { + return workbookDescriptorRepo.getAll().blockingGet() + .firstOrNull { wb -> + val sourceVersionMatches = resourceVersion?.slug?.let { + wb.sourceCollection.resourceContainer!!.identifier == it + } != false + + wb.sourceLanguage == sourceLanguage && + wb.targetLanguage == targetLanguage && + sourceVersionMatches + }?.let { + WorkbookDescriptorWrapper(it) + } + } + /** * Blocks the execution of project creation until projects delete queue completes. */ @@ -326,12 +363,4 @@ class ProjectWizardViewModel : ViewModel() { targetLanguages.clear() resourceVersions.clear() } - - private fun updateExistingLanguagePairs() { - val homePageViewModel = find() - val languagePairs = homePageViewModel.projectGroups.map { - Pair(it.sourceLanguage, it.targetLanguage) - } - existingLanguagePairs.setAll(languagePairs) - } } From fbe63f72afde7474b8fb5fb749df29bfb0646901 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 15 Aug 2024 15:00:51 -0400 Subject: [PATCH 08/11] accessiblity --- .../tableview/ResourceVersionTableView.kt | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/components/tableview/ResourceVersionTableView.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/components/tableview/ResourceVersionTableView.kt index 3476e249a8..791fa631ec 100644 --- a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/components/tableview/ResourceVersionTableView.kt +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/components/tableview/ResourceVersionTableView.kt @@ -21,8 +21,11 @@ package org.wycliffeassociates.otter.jvm.workbookapp.ui.components.tableview import javafx.collections.ObservableList import javafx.event.EventTarget import javafx.scene.control.TableView +import javafx.scene.input.KeyCode +import javafx.scene.input.KeyEvent import javafx.scene.layout.Priority import javafx.scene.layout.Region +import org.wycliffeassociates.otter.jvm.controls.event.ResourceVersionSelectedEvent import org.wycliffeassociates.otter.jvm.controls.model.ResourceVersion import tornadofx.* import tornadofx.FX.Companion.messages @@ -56,7 +59,20 @@ class ResourceVersionTableView( setRowFactory { ResourceVersionTableRow() } - //TODO: accessibility support + /* accessibility */ + focusedProperty().onChange { + if (it && selectionModel.selectedIndex < 0) { + selectionModel.select(0) + focusModel.focus(0) + } + } + addEventFilter(KeyEvent.KEY_PRESSED) { keyEvent -> + if (keyEvent.code == KeyCode.SPACE || keyEvent.code == KeyCode.ENTER) { + selectedItem?.let { + FX.eventbus.fire(ResourceVersionSelectedEvent(it)) + } + } + } } } From e8307a4af356f9f31cd7a6d1e649e32d48c914da Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 15 Aug 2024 15:01:51 -0400 Subject: [PATCH 09/11] rename --- .../jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt index cbb4558f74..e67a48a90f 100644 --- a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt @@ -245,7 +245,7 @@ class ProjectWizardViewModel : ViewModel() { logger.info("Creating project group: ${sourceLanguage.name} - ${targetLanguage.name}") isLoadingProperty.set(true) - val existingBook = fetchExistingWorkBook(resourceVersion, sourceLanguage, targetLanguage) + val existingBook = findExistingWorkBook(resourceVersion, sourceLanguage, targetLanguage) // if the project is already created, bookmark it to open after home page finishes loading if (existingBook != null) { bookMarkedProjectGroupProperty.set( @@ -323,7 +323,7 @@ class ProjectWizardViewModel : ViewModel() { fun increaseProjectDeleteCounter() { projectDeleteCounter.incrementAndGet() } fun decreaseProjectDeleteCounter() { projectDeleteCounter.decrementAndGet() } - private fun fetchExistingWorkBook( + private fun findExistingWorkBook( resourceVersion: ResourceVersion?, sourceLanguage: Language, targetLanguage: Language From 79c20a36bc81e02b10b39678f907578753f25bf1 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 15 Aug 2024 15:03:26 -0400 Subject: [PATCH 10/11] switch case naming --- .../workbookapp/ui/viewmodel/ProjectWizardViewModel.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt index e67a48a90f..1e887e4dba 100644 --- a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/ProjectWizardViewModel.kt @@ -170,15 +170,15 @@ class ProjectWizardViewModel : ViewModel() { availableVersions .subscribe { versions -> val ignoreVersionSelect = versions.size == 1 - val createNarrationProject = ignoreVersionSelect && projectMode == ProjectMode.NARRATION - val createOtherProject = ignoreVersionSelect && sourceLanguage != null + val quickCreateNarration = ignoreVersionSelect && projectMode == ProjectMode.NARRATION + val quickCreateProject = ignoreVersionSelect && sourceLanguage != null when { - createNarrationProject -> { + quickCreateNarration -> { createProject(language, language, resourceVersion = null, onNavigateBack) } - createOtherProject -> { + quickCreateProject -> { createProject(sourceLanguage, language, resourceVersion = null, onNavigateBack) } From 9113f266820cd3fd6eeeacb13fdcedd9f99b1b68 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 16 Aug 2024 08:47:08 -0400 Subject: [PATCH 11/11] remove commented code --- .../otter/jvm/workbookapp/ui/viewmodel/HomePageViewModel2.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/HomePageViewModel2.kt b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/HomePageViewModel2.kt index 482be7e692..9ed007f9db 100644 --- a/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/HomePageViewModel2.kt +++ b/jvm/workbookapp/src/main/kotlin/org/wycliffeassociates/otter/jvm/workbookapp/ui/viewmodel/HomePageViewModel2.kt @@ -210,9 +210,6 @@ class HomePageViewModel2 : ViewModel() { .observeOnFx() .doOnComplete { logger.info("Deleted project group: ${cardModel.sourceLanguage.name} -> ${cardModel.targetLanguage.name}.") -// projectWizardViewModel.existingLanguagePairs.remove( -// Pair(cardModel.sourceLanguage, cardModel.targetLanguage) -// ) } .doOnDispose { logger.info("Cancelled deleting project group ${cardModel.sourceLanguage.name} -> ${cardModel.targetLanguage.name}.")