Skip to content

Commit

Permalink
Update Reschedule Story Event flow
Browse files Browse the repository at this point in the history
  • Loading branch information
b-camphart committed Nov 10, 2021
1 parent 61f6627 commit da91569
Show file tree
Hide file tree
Showing 25 changed files with 247 additions and 789 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,51 @@ package com.soyle.stories.storyevent.time.reschedule

import com.soyle.stories.common.ThreadTransformer
import com.soyle.stories.domain.storyevent.StoryEvent
import com.soyle.stories.storyevent.time.NormalizationPrompt
import com.soyle.stories.usecase.storyevent.StoryEventRepository
import com.soyle.stories.usecase.storyevent.time.reschedule.RescheduleStoryEvent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.coroutines.CoroutineContext

interface RescheduleStoryEventController {

fun requestToRescheduleStoryEvent(storyEventId: StoryEvent.Id, currentTime: Long)
fun rescheduleStoryEvent(storyEventId: StoryEvent.Id, time: Long): Job
fun rescheduleStoryEvent(storyEventId: StoryEvent.Id): Job

companion object {
fun Implementation(
guiContext: CoroutineContext,
asyncContext: CoroutineContext,

operator fun invoke(
threadTransformer: ThreadTransformer,
rescheduleStoryEvent: RescheduleStoryEvent,
rescheduleStoryEventOutput: RescheduleStoryEvent.OutputPort,
newTimePrompt: RescheduleStoryEventPrompt,
normalizationPrompt: NormalizationPrompt,

newTimePrompt: RescheduleStoryEventPrompt
): RescheduleStoryEventController = object : RescheduleStoryEventController {
storyEventRepository: StoryEventRepository,

override fun requestToRescheduleStoryEvent(storyEventId: StoryEvent.Id, currentTime: Long) {
newTimePrompt.promptForNewTime(storyEventId, currentTime)
rescheduleStoryEvent: RescheduleStoryEvent,
rescheduleStoryEventOutput: RescheduleStoryEvent.OutputPort,
): RescheduleStoryEventController = object : RescheduleStoryEventController, CoroutineScope by CoroutineScope(guiContext) {
override fun rescheduleStoryEvent(storyEventId: StoryEvent.Id): Job = launch {
val storyEvent = storyEventRepository.getStoryEventOrError(storyEventId)
newTimePrompt.use {
rescheduleStoryEvent(storyEventId, storyEvent.time.toLong())
}
}

override fun rescheduleStoryEvent(storyEventId: StoryEvent.Id, time: Long): Job {
return threadTransformer.async {
rescheduleStoryEvent.invoke(storyEventId, time, rescheduleStoryEventOutput)
private tailrec suspend fun rescheduleStoryEvent(storyEventId: StoryEvent.Id, currentTime: Long) {
val newTime = newTimePrompt.requestNewTime(currentTime) ?: return

if (newTime > 0 || normalizationPrompt.confirmNormalization()) {
withContext(asyncContext) {
rescheduleStoryEvent(storyEventId, newTime, rescheduleStoryEventOutput)
}
return
}
rescheduleStoryEvent(storyEventId, currentTime)
}

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ package com.soyle.stories.storyevent.time.reschedule

import com.soyle.stories.domain.storyevent.StoryEvent

interface RescheduleStoryEventPrompt {
fun promptForNewTime(storyEventId: StoryEvent.Id, currentTime: Long)
interface RescheduleStoryEventPrompt : AutoCloseable {
suspend fun requestNewTime(currentTime: Long): Long?
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,8 @@
package com.soyle.stories.storyevent.time.reschedule

import com.soyle.stories.domain.storyevent.StoryEvent
import io.mockk.spyk
import kotlinx.coroutines.Job


class RescheduleStoryEventControllerDouble : RescheduleStoryEventController {

override fun requestToRescheduleStoryEvent(storyEventId: StoryEvent.Id, currentTime: Long) {

}

override fun rescheduleStoryEvent(storyEventId: StoryEvent.Id, time: Long): Job {
return Job()
}
}
class RescheduleStoryEventControllerDouble : RescheduleStoryEventController by spyk()
30 changes: 2 additions & 28 deletions desktop/src/main/kotlin/storyevent/Presentation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import com.soyle.stories.storyevent.rename.*
import com.soyle.stories.storyevent.time.*
import com.soyle.stories.storyevent.time.adjust.AdjustStoryEventsTimeController
import com.soyle.stories.storyevent.time.adjust.AdjustStoryEventsTimePrompt
import com.soyle.stories.storyevent.time.adjust.StoryEventTimeChangePromptPresenter
import com.soyle.stories.storyevent.time.StoryEventTimeChangePromptPresenter
import com.soyle.stories.storyevent.time.normalization.NormalizationPromptPresenter
import com.soyle.stories.storyevent.time.reschedule.RescheduleStoryEventController
import com.soyle.stories.storyevent.time.reschedule.RescheduleStoryEventPrompt
Expand All @@ -43,7 +43,6 @@ import com.soyle.stories.storyevent.timeline.viewport.ruler.label.TimeSpanLabel
import com.soyle.stories.storyevent.timeline.viewport.ruler.label.TimeSpanLabelComponent
import com.soyle.stories.storyevent.timeline.viewport.ruler.label.menu.TimelineRulerLabelMenu
import com.soyle.stories.storyevent.timeline.viewport.ruler.label.menu.TimelineRulerLabelMenuComponent
import com.soyle.stories.usecase.storyevent.create.CreateStoryEvent
import javafx.beans.property.BooleanProperty
import javafx.collections.ObservableList
import javafx.scene.Node
Expand Down Expand Up @@ -136,35 +135,10 @@ object Presentation {
}
}

provide<AdjustStoryEventsTimePrompt> {
provide(RescheduleStoryEventPrompt::class, AdjustStoryEventsTimePrompt::class) {
StoryEventTimeChangePromptPresenter(get<WorkBench>()::currentStage)
}

provide(RescheduleStoryEventPrompt::class) {
object : RescheduleStoryEventPrompt {

private val presenterBuilder by lazy {
TimeAdjustmentPromptPresenter(
get(),
get(),
applicationScope.get()
)
}

override fun promptForNewTime(storyEventId: StoryEvent.Id, currentTime: Long) {
val presenter = presenterBuilder(storyEventId, currentTime)

val stage = TimeAdjustmentPromptView(
presenter,
presenter.viewModel
).openModal(owner = get<WorkBench>().root.scene?.window)!!
presenter.viewModel.isCompleted.onChangeUntil({ it == true }) {
if (it == true) stage.hide()
}
}
}
}

provide<RemoveStoryEventConfirmation> {
object : RemoveStoryEventConfirmation {
override fun requestDeleteStoryEventConfirmation(storyEventIds: Set<StoryEvent.Id>) {
Expand Down
7 changes: 5 additions & 2 deletions desktop/src/main/kotlin/storyevent/UseCases.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,11 @@ object UseCases {
provide<RescheduleStoryEvent> { RescheduleStoryEventUseCase(get()) }
provide<RescheduleStoryEvent.OutputPort> { RescheduleStoryEventOutput(get()) }
provide<RescheduleStoryEventController> {
RescheduleStoryEventController(
applicationScope.get(),
RescheduleStoryEventController.Implementation(
applicationScope.get<ThreadTransformer>().guiContext,
applicationScope.get<ThreadTransformer>().asyncContext,
get(),
get(),
get(),
get(),
get()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ package com.soyle.stories.desktop.config.drivers.storyevent
import com.soyle.stories.desktop.config.drivers.robot
import com.soyle.stories.desktop.view.project.workbench.getOpenDialog
import com.soyle.stories.project.WorkBench
import com.soyle.stories.storyevent.time.TimeAdjustmentPromptView
import com.soyle.stories.storyevent.time.StoryEventTimeChangeView
import javafx.scene.control.Spinner

@Suppress("unused")
fun WorkBench.getOpenStoryEventTimeAdjustmentDialog(): TimeAdjustmentPromptView? =
fun WorkBench.getOpenStoryEventTimeAdjustmentDialog(): StoryEventTimeChangeView? =
robot.getOpenDialog()

fun WorkBench.getOpenStoryEventTimeAdjustmentDialogOrError(): TimeAdjustmentPromptView =
fun WorkBench.getOpenStoryEventTimeAdjustmentDialogOrError(): StoryEventTimeChangeView =
getOpenStoryEventTimeAdjustmentDialog() ?: error("Reschedule Story Event Dialog is not open")

fun TimeAdjustmentPromptView.reschedule(to: Long) {
fun StoryEventTimeChangeView.reschedule(to: Long) {
robot.interact {
val timeInput = robot.from(root).lookup("#time").query<Spinner<Long?>>()
timeInput.editor.text = to.toString()
Expand All @@ -22,7 +22,7 @@ fun TimeAdjustmentPromptView.reschedule(to: Long) {
}
}

fun TimeAdjustmentPromptView.adjustTime(by: Long) {
fun StoryEventTimeChangeView.adjustTime(by: Long) {
robot.interact {
val timeInput = robot.from(root).lookup("#time").query<Spinner<Long?>>()
timeInput.editor.text = by.toString()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
package com.soyle.stories.desktop.view.storyevent

import com.soyle.stories.desktop.view.testframework.DesignTest
import com.soyle.stories.storyevent.time.TimeAdjustmentPromptView
import com.soyle.stories.storyevent.time.TimeAdjustmentPromptViewActions
import com.soyle.stories.storyevent.time.TimeAdjustmentPromptViewModel
import com.soyle.stories.storyevent.time.StoryEventTimeChangeView
import com.soyle.stories.storyevent.time.StoryEventTimeChangeViewModel
import com.soyle.stories.storyevent.time.adjust.AdjustTimePromptViewModel
import com.soyle.stories.storyevent.time.reschedule.ReschedulePromptViewModel
import javafx.scene.Node
import org.junit.jupiter.api.Test

class `Time Adjustment Prompt Design` : DesignTest() {

private val actions = object : TimeAdjustmentPromptViewActions {
override fun submit() = Unit
override fun cancel() = Unit
}

private var viewModel: TimeAdjustmentPromptViewModel = TimeAdjustmentPromptViewModel.adjustment()
private var viewModel: StoryEventTimeChangeViewModel = AdjustTimePromptViewModel()
override val node: Node
get() = TimeAdjustmentPromptView(actions, viewModel).root
get() = StoryEventTimeChangeView(viewModel).root

@Test
fun `created without current time`() {
Expand All @@ -25,13 +21,13 @@ class `Time Adjustment Prompt Design` : DesignTest() {

@Test
fun `created with current time`() {
viewModel = TimeAdjustmentPromptViewModel.reschedule(9L)
viewModel = ReschedulePromptViewModel(9L)
verifyDesign()
}

@Test
fun submitting() {
viewModel = TimeAdjustmentPromptViewModel.reschedule(4)
viewModel = ReschedulePromptViewModel(4)
viewModel.submitting()
verifyDesign()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class StoryEventItemMenuPresenter(

override fun rescheduleSelectedItem() {
singleSelection {
dependencies.rescheduleStoryEventController.requestToRescheduleStoryEvent(it.storyEventId, it.time)
dependencies.rescheduleStoryEventController.rescheduleStoryEvent(it.storyEventId)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,8 @@ class StoryEventListPresenter(

override fun rescheduleSelectedItem() {
selectedItem {
rescheduleStoryEventController.requestToRescheduleStoryEvent(
it.id,
it.timeProperty.value
rescheduleStoryEventController.rescheduleStoryEvent(
it.id
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.soyle.stories.storyevent.time

import com.soyle.stories.storyevent.time.adjust.AdjustStoryEventsTimePrompt
import com.soyle.stories.storyevent.time.adjust.AdjustTimePromptViewModel
import com.soyle.stories.storyevent.time.reschedule.ReschedulePromptViewModel
import com.soyle.stories.storyevent.time.reschedule.RescheduleStoryEventPrompt
import javafx.beans.binding.BooleanExpression
import javafx.stage.Modality
import javafx.stage.Window
import kotlinx.coroutines.CompletableDeferred
import tornadofx.booleanBinding
import tornadofx.booleanProperty

class StoryEventTimeChangePromptPresenter(
private val getOwnerWindow: () -> Window?
) : AdjustStoryEventsTimePrompt, RescheduleStoryEventPrompt {

private var viewModel: StoryEventTimeChangeViewModel? = null
private var view: StoryEventTimeChangeView? = null

override suspend fun requestAdjustmentAmount(): Long? {
val deferred = CompletableDeferred<Long?>()
val viewModel = adjustmentViewModel()
openWindowIfNotAlready(deferred)
viewModel.endSubmission()

viewModel.setOnSubmit {
if (! deferred.isCompleted) deferred.complete(viewModel.time)
}
return deferred.await()
}

override suspend fun confirmAdjustmentAmount(amount: Long): Long? {
val deferred = CompletableDeferred<Long?>()
val viewModel = adjustmentViewModel()
openWindowIfNotAlready(deferred)
viewModel.endSubmission()
viewModel.time = amount
viewModel.setOnSubmit {
if (! deferred.isCompleted) deferred.complete(viewModel.time)
}
return deferred.await()
}

override suspend fun requestNewTime(currentTime: Long): Long? {
val deferred = CompletableDeferred<Long?>()
val viewModel = rescheduleViewModel(currentTime)
openWindowIfNotAlready(deferred)
viewModel.endSubmission()
viewModel.setOnSubmit {
if (! deferred.isCompleted) deferred.complete(viewModel.time)
}
return deferred.await()
}

private fun adjustmentViewModel(): StoryEventTimeChangeViewModel {
var viewModel = viewModel as? AdjustTimePromptViewModel
if (viewModel == null) {
viewModel = AdjustTimePromptViewModel()
view = StoryEventTimeChangeView(viewModel)
this.viewModel = viewModel
}
return viewModel
}

private fun rescheduleViewModel(currentTime: Long): StoryEventTimeChangeViewModel {
var viewModel = viewModel as? ReschedulePromptViewModel
if (viewModel == null) {
viewModel = ReschedulePromptViewModel(currentTime)
view = StoryEventTimeChangeView(viewModel)
this.viewModel = viewModel
}
return viewModel
}

private fun openWindowIfNotAlready(deferred: CompletableDeferred<Long?>) {
if (view?.currentStage?.isShowing != true) view?.openModal(modality = Modality.APPLICATION_MODAL, owner = getOwnerWindow())
view?.currentStage?.setOnHidden {
if (! deferred.isCompleted) deferred.complete(null)

viewModel = null
view = null
}
}

override fun close() {
view?.currentStage?.close()
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.soyle.stories.storyevent.time.adjust
package com.soyle.stories.storyevent.time

import com.soyle.stories.common.components.ComponentsStyles
import com.soyle.stories.common.components.text.FieldLabel.Companion.fieldLabel
Expand All @@ -25,7 +25,7 @@ class StoryEventTimeChangeView(
id = "time"
valueFactory = NullableLongSpinnerValueFactory()
isEditable = true
viewModel.adjustment().bindBidirectional(editor.textProperty())
viewModel.timeText().bindBidirectional(editor.textProperty())
disableWhen(viewModel.submitting())
editor.action(viewModel::submit)
}
Expand Down
Loading

0 comments on commit da91569

Please sign in to comment.