Skip to content

Commit

Permalink
Takes View Model- JVM (#458)
Browse files Browse the repository at this point in the history
* Resource takes page view

* Test app for resource take cards

* Added test wav files

* Renamed test app

* Takes view model

* Load test text and takes

* Navigate to takes from resources list (load real data)

* TakesListView and Play/Pause event handling

* Takes page refactor

Fix broken Takes Fragment

* Support change to Resource

* Refactoring to support RecordableItem

* Use observable list of Recordables rather than 'title' and 'body'

* Better names for Takes fragments

* WorkbookViewModel

* navigateToTakesPage() uses BookElement

* Support change to RecordTake

* New take action

* Put tab label properties in an enum map

* Add sort to tabs

* Fixed bug in TakeDao

* Proper subscribe handling

* Add vgrow to workspace root to fill screen

* Dispose takes subscription when Recordable changes

* Remove test files to be put in test branch

* WorkbookViewModel getters return non-null values or throw error

* Remove code dependent on testapp files

* Utility function for EnumMap: getNotNull()

* Bug fix for TakesTab clear disposables

* Restore functionality to TMVM recordContent (sort of)

TakeManagementViewModel will be merged with TakesViewModel soon, so these changes will be obsolete when that happens.

* PR Comments

* PR Comments

* PR Comments and applying FileName pattern

* Using Recordable Interface and WorkbookFileNamerBuilder

* Simplified WorkbookFileNameBuilder

* Using simplified FileNamer

* Better filenames

* Renamed to RecordResourceViewModel

Moved recording functionality to TakeManagementViewModel (which needs to be refactored later)

* Removed Platform.runLater where it is not necessary

* Refactored RecordableTab, added RecordableTabViewModel

* Minor improvements

* ResourcesViewModel tests and RecordableTab tests

* Allow importing OBS markdown files (#461)

* Allow importing OBS markdown files.

MimeType enum instead of string literals.
Ignore creator if unable to match help RCs otherwise.
Unhide the Tree generics.
More type safety.

* Narrow down a few more OtterTree<Any> params.

* Test cleanup

* Fix broken unit tests

* Update travis.yml to include chromeabletabpane

* Remove RecordableTabTest until better solution found

It used PlatformImpl.startup, which errors on travis
  • Loading branch information
KJoslyn authored and jsarabia committed Jul 2, 2019
1 parent e2df174 commit 14d399d
Show file tree
Hide file tree
Showing 27 changed files with 877 additions and 80 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ cache:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper
install:
- git clone --branch=jbs-otter-css https://github.com/WycliffeAssociates/chromeabletabpane.git ../chromeabletabpane
- git clone --branch=dev https://github.com/WycliffeAssociates/kotlin-resource-container.git ../kotlin-resource-container
- git clone https://github.com/WycliffeAssociates/otter-common.git ../otter-common
- export BRANCH=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)
Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ dependencies {
//WA Libraries
implementation 'org.wycliffeassociates:otter-common'
implementation 'org.wycliffeassociates:kotlin-resource-container'
implementation 'org.wycliffeassociates:chromeabletabpane'
implementation 'com.github.WycliffeAssociates:jdenticon-kotlin:-SNAPSHOT'

//Atlassian commonmark (for rendering markdown)
Expand Down
3 changes: 2 additions & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
rootProject.name = 'otter-jvm'
includeBuild '../kotlin-resource-container'
includeBuild '../otter-common'
includeBuild '../chromeabletabpane'
includeBuild '../otter-common'
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.wycliffeassociates.otter.jvm.app.ui.mainscreen.view

import javafx.scene.paint.Color
import javafx.stage.Screen
import org.wycliffeassociates.otter.jvm.app.theme.AppTheme
import tornadofx.*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import org.wycliffeassociates.otter.jvm.app.widgets.workbookheader.workbookheade
import org.wycliffeassociates.otter.jvm.app.widgets.resourcecard.styles.ResourceListStyles
import org.wycliffeassociates.otter.jvm.app.widgets.resourcecard.view.ResourceListView
import org.wycliffeassociates.otter.jvm.app.ui.resources.viewmodel.ResourcesViewModel
import org.wycliffeassociates.otter.jvm.app.ui.workbook.viewmodel.WorkbookViewModel
import tornadofx.*

class ResourceListFragment : Fragment() {
val viewModel: ResourcesViewModel by inject()
private val workbookViewModel: WorkbookViewModel by inject()
private val resourcesViewModel: ResourcesViewModel by inject()

init {
importStylesheet<MainScreenStyles>()
Expand All @@ -20,12 +22,12 @@ class ResourceListFragment : Fragment() {

add(
workbookheader {
labelText = "${viewModel.chapter.title} ${messages["resources"]}"
labelText = "${workbookViewModel.chapter.title} ${messages["resources"]}"
filterText = messages["hideCompleted"]
}
)
add(
ResourceListView(viewModel.resourceGroups)
ResourceListView(resourcesViewModel.resourceGroupCardItemList)
)
}
}
Original file line number Diff line number Diff line change
@@ -1,43 +1,38 @@
package org.wycliffeassociates.otter.jvm.app.ui.resources.viewmodel

import javafx.application.Platform
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import org.wycliffeassociates.otter.common.data.workbook.*
import org.wycliffeassociates.otter.common.utils.mapNotNull
import org.wycliffeassociates.otter.jvm.app.ui.resourcetakes.viewmodel.RecordResourceViewModel
import org.wycliffeassociates.otter.jvm.app.ui.workbook.viewmodel.WorkbookViewModel
import org.wycliffeassociates.otter.jvm.app.widgets.resourcecard.model.ResourceGroupCardItemList
import org.wycliffeassociates.otter.jvm.app.widgets.resourcecard.model.resourceGroupCardItem
import tornadofx.*

class ResourcesViewModel : ViewModel() {
val activeWorkbookProperty = SimpleObjectProperty<Workbook>()
val workbook: Workbook
get() = activeWorkbookProperty.value
internal val recordResourceViewModel: RecordResourceViewModel by inject()
private val workbookViewModel: WorkbookViewModel by inject()

val activeChapterProperty = SimpleObjectProperty<Chapter>()
val chapter: Chapter
get() = activeChapterProperty.value

val activeResourceSlugProperty = SimpleStringProperty()
val resourceSlug: String
get() = activeResourceSlugProperty.value

val resourceGroups: ResourceGroupCardItemList = ResourceGroupCardItemList(mutableListOf())
val resourceGroupCardItemList: ResourceGroupCardItemList = ResourceGroupCardItemList()

fun loadResourceGroups() {
val chapter = workbookViewModel.chapter
chapter
.children
.startWith(chapter)
.mapNotNull { resourceGroupCardItem(it, resourceSlug, onSelect = this::navigateToTakesPage) }
.mapNotNull {
resourceGroupCardItem(it, workbookViewModel.resourceSlug, onSelect = this::navigateToTakesPage)
}
.buffer(2) // Buffering by 2 prevents the list UI from jumping while groups are loading
.subscribe {
Platform.runLater {
resourceGroups.addAll(it)
}
resourceGroupCardItemList.addAll(it)
}
}

private fun navigateToTakesPage(resource: Resource) {
// TODO use navigator
internal fun navigateToTakesPage(bookElement: BookElement, resource: Resource) {
// TODO use navigator to navigate to takes page
workbookViewModel.activeChunkProperty.set(bookElement as? Chunk)
recordResourceViewModel.setRecordableListItems(
listOfNotNull(resource.title, resource.body)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package org.wycliffeassociates.otter.jvm.app.ui.resourcetakes.view

import javafx.geometry.Pos
import javafx.scene.layout.BorderStrokeStyle
import javafx.scene.paint.Color
import javafx.scene.text.FontPosture
import org.wycliffeassociates.otter.jvm.app.theme.AppTheme
import tornadofx.*

typealias LinearU = Dimension<Dimension.LinearUnits>

class RecordResourceStyles : Stylesheet() {
companion object {
val takesTab by cssclass()
val leftRegionContainer by cssclass()
val rightRegion by cssclass()
val dragTarget by cssclass()
val contentText by cssclass()
val newTakeRegion by cssclass()
val contentScrollPane by cssclass()
val takesList by cssclass()

val takeMaxWidth = 500.px
val takeMinHeight = 80.px
}

private fun margin(top: LinearU, right: LinearU, bottom: LinearU, left: LinearU) = mixin {
backgroundInsets += box(top, right, bottom, left)
borderInsets += box(top, right, bottom, left)
}

private val topMargin = 15.px
private val bottomMargin = 15.px
private val leftRegionLeftMargin = 80.px
private val leftRegionRightMargin = 100.px

init {
takesTab {
backgroundColor += AppTheme.colors.white
}

leftRegionContainer {
borderColor += box(
Color.TRANSPARENT,
AppTheme.colors.lightBackground,
AppTheme.colors.lightBackground,
Color.TRANSPARENT
)
}

rightRegion {
padding = box(topMargin, 70.px, 30.px, 30.px)
}

dragTarget {
+margin(topMargin, leftRegionLeftMargin, 0.px, leftRegionRightMargin)
alignment = Pos.CENTER
backgroundColor += AppTheme.colors.defaultBackground
maxWidth = takeMaxWidth + leftRegionLeftMargin + leftRegionRightMargin
minHeight = takeMinHeight + topMargin
borderStyle += BorderStrokeStyle.DASHED
borderWidth += box(2.px)
fontStyle = FontPosture.ITALIC
}

contentText {
fontSize = 24.px // If you put this in contentScrollPane, the scroll bar gets very big
}

newTakeRegion {
alignment = Pos.CENTER
borderColor += box(AppTheme.colors.lightBackground, Color.TRANSPARENT, Color.TRANSPARENT, Color.TRANSPARENT)
borderWidth += box(3.px, 0.px, 0.px, 0.px)
padding = box(topMargin, leftRegionLeftMargin, bottomMargin, leftRegionRightMargin)
}

contentScrollPane {
padding = box(15.px, leftRegionLeftMargin, 30.px, leftRegionRightMargin)
backgroundColor += Color.TRANSPARENT
viewport {
backgroundColor += Color.TRANSPARENT
}
}

takesList {
backgroundColor += Color.TRANSPARENT
}

listCell {
backgroundColor += Color.TRANSPARENT
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.wycliffeassociates.otter.jvm.app.ui.resourcetakes.view

import org.wycliffeassociates.controls.ChromeableTabPane
import org.wycliffeassociates.otter.common.data.model.ContentType
import org.wycliffeassociates.otter.jvm.app.ui.resourcetakes.viewmodel.RecordResourceViewModel
import tornadofx.*
import org.wycliffeassociates.otter.jvm.utils.getNotNull

class RecordResourceView : View() {
private val viewModel: RecordResourceViewModel by inject()
private val tabPane = ChromeableTabPane()

override val root = tabPane

// The tabs will add or remove themselves from the tabPane when their view model's 'recordable' property changes
private val tabs: List<RecordableTab> = listOf(
recordableTab(ContentType.TITLE, 0),
recordableTab(ContentType.BODY, 1)
)

private fun recordableTab(contentType: ContentType, sort: Int) = RecordableTab(
viewModel.contentTypeToViewModelMap.getNotNull(contentType),
tabPane,
sort,
viewModel::onTabSelect
)

init {
importStylesheet<RecordResourceStyles>()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package org.wycliffeassociates.otter.jvm.app.ui.resourcetakes.view

import javafx.scene.control.Tab
import javafx.scene.control.TabPane
import org.wycliffeassociates.otter.common.domain.content.Recordable
import org.wycliffeassociates.otter.jvm.app.ui.resourcetakes.viewmodel.RecordableTabViewModel
import tornadofx.*
import kotlin.math.min

class RecordableTab(
private val viewModel: RecordableTabViewModel,
// tabPaneProperty gets set to null every time the tab gets removed from the tab pane so we need to cache it
private val parent: TabPane,
val sort: Int,
private val onTabSelect: (Recordable) -> Unit
): Tab() {

init {
textProperty().bind(viewModel.labelProperty)

RecordableTabContent(viewModel.takesList).apply {
formattedTextProperty.bind(viewModel.getFormattedTextBinding())
this@RecordableTab.content = this.root
}

selectedProperty().onChange { selected ->
if (selected) {
callOnTabSelect()
}
}

viewModel.recordableProperty.onChange { item ->
item?.let {
checkAndAddSelf()
} ?: removeSelf()
}
}

private fun checkAndAddSelf() {
if (!parent.tabs.contains(this)) {
addSelfToParent()
}
}

private fun removeSelf() {
parent.tabs.remove(this)
}

private fun addSelfToParent() {
parent.tabs.add(min(sort, parent.tabs.size), this)
if (parent.tabs.size == 1) {
selectTab()
}
}

private fun selectTab() {
select()
callOnTabSelect()
}

private fun callOnTabSelect() {
viewModel.recordable?.let { onTabSelect(it) }
?: throw IllegalStateException("Selected tab's recordable is null")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.wycliffeassociates.otter.jvm.app.ui.resourcetakes.view

import javafx.beans.property.SimpleStringProperty
import javafx.collections.ObservableList
import javafx.scene.layout.GridPane
import javafx.scene.layout.RowConstraints
import org.wycliffeassociates.otter.common.data.workbook.Take
import org.wycliffeassociates.otter.jvm.app.ui.resourcetakes.viewmodel.RecordResourceViewModel
import org.wycliffeassociates.otter.jvm.app.ui.takemanagement.viewmodel.TakeManagementViewModel
import tornadofx.*

class RecordableTabContent(
takesList: ObservableList<Take>
) : Fragment() {

private val takeManagementViewModel: TakeManagementViewModel by inject()
private val recordResourceViewModel: RecordResourceViewModel by inject()

val formattedTextProperty = SimpleStringProperty()

init {
importStylesheet<RecordResourceStyles>()
}

private fun GridPane.setFillHeightSingleRow() {
val rc = RowConstraints()
rc.percentHeight = 100.0
rowConstraints.addAll(rc)
}

override val root = gridpane {
addClass(RecordResourceStyles.takesTab)
setFillHeightSingleRow()

row {
vbox {
gridpaneColumnConstraints {
percentWidth = 50.0
}
addClass(RecordResourceStyles.leftRegionContainer)
add(
TabContentLeftRegion(formattedTextProperty, recordResourceViewModel::newTakeAction)
)
}
vbox(20.0) {
gridpaneColumnConstraints {
percentWidth = 50.0
}
addClass(RecordResourceStyles.rightRegion)
add(
TakesListView(takesList, takeManagementViewModel::audioPlayer)
)
}
}
}
}
Loading

0 comments on commit 14d399d

Please sign in to comment.