-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Moved TaskManager component to scommons-react-redux module
- Loading branch information
1 parent
65321b9
commit 7d1069d
Showing
5 changed files
with
426 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
144 changes: 144 additions & 0 deletions
144
redux/src/main/scala/scommons/react/redux/task/TaskManager.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
package scommons.react.redux.task | ||
|
||
import scommons.react._ | ||
import scommons.react.hooks._ | ||
|
||
import scala.scalajs.js | ||
import scala.scalajs.js.{Error, JavaScriptException} | ||
import scala.util.{Failure, Success, Try} | ||
|
||
case class TaskManagerProps(startTask: Option[AbstractTask]) | ||
|
||
/** | ||
* Handles status of running tasks. | ||
*/ | ||
object TaskManager extends FunctionComponent[TaskManagerProps] { | ||
|
||
var uiComponent: UiComponent[TaskManagerUiProps] = _ | ||
|
||
var errorHandler: PartialFunction[Try[_], (Option[String], Option[String])] = PartialFunction.empty | ||
|
||
private case class TaskManagerState(taskCount: Int = 0, | ||
status: Option[String] = None, | ||
error: Option[String] = None, | ||
errorDetails: Option[String] = None) | ||
|
||
protected def render(compProps: Props): ReactElement = { | ||
val props = compProps.wrapped | ||
val (state, setState) = useStateUpdater(() => TaskManagerState()) | ||
|
||
if (uiComponent == null) { | ||
throw JavaScriptException(Error("TaskManager.uiComponent is not specified")) | ||
} | ||
|
||
useEffect({ () => | ||
props.startTask.foreach { task => | ||
onTaskStart(setState, task) | ||
} | ||
}, List(props.startTask match { | ||
case None => js.undefined | ||
case Some(task) => task.asInstanceOf[js.Any] | ||
})) | ||
|
||
<(uiComponent())(^.wrapped := TaskManagerUiProps( | ||
showLoading = state.taskCount > 0, | ||
status = state.status, | ||
onHideStatus = { () => | ||
setState(_.copy(status = None)) | ||
}, | ||
error = state.error, | ||
errorDetails = state.errorDetails, | ||
onCloseErrorPopup = { () => | ||
setState(_.copy(error = None, errorDetails = None)) | ||
} | ||
))() | ||
} | ||
|
||
private def onTaskStart(setState: js.Function1[js.Function1[TaskManagerState, TaskManagerState], Unit], | ||
task: AbstractTask): Unit = { | ||
|
||
task.onComplete { value: Try[_] => | ||
onTaskFinish(setState, task, value) | ||
} | ||
|
||
setState(s => s.copy( | ||
taskCount = s.taskCount + 1, | ||
status = Some(s"${task.message}...") | ||
)) | ||
} | ||
|
||
private def onTaskFinish(setState: js.Function1[js.Function1[TaskManagerState, TaskManagerState], Unit], | ||
task: AbstractTask, | ||
value: Try[_]): Unit = { | ||
|
||
val durationMillis = System.currentTimeMillis() - task.startTime | ||
val statusMessage = s"${task.message}...Done ${formatDuration(durationMillis)} sec." | ||
|
||
def defaultErrorHandler(value: Try[_]): (Option[String], Option[String]) = value match { | ||
case Success(_) => (None, None) | ||
case Failure(e) => (Some(e.toString), Some(printStackTrace(e))) | ||
} | ||
|
||
val (error, errorDetails) = errorHandler.applyOrElse(value, defaultErrorHandler) | ||
|
||
setState(s => s.copy( | ||
taskCount = s.taskCount - 1, | ||
status = Some(statusMessage), | ||
error = error, | ||
errorDetails = errorDetails | ||
)) | ||
} | ||
|
||
private[task] def formatDuration(durationMillis: Long): String = { | ||
"%.3f".format(durationMillis / 1000.0) | ||
} | ||
|
||
private[task] def printStackTrace(x: Throwable): String = { | ||
val sb = new StringBuilder(x.toString) | ||
val trace = x.getStackTrace | ||
for (t <- trace) { | ||
sb.append("\n\tat ").append(t) | ||
} | ||
|
||
val cause = x.getCause | ||
if (cause != null) { | ||
printStackTraceAsCause(sb, cause, trace) | ||
} | ||
|
||
sb.toString | ||
} | ||
|
||
/** | ||
* Print stack trace as a cause for the specified stack trace. | ||
*/ | ||
private def printStackTraceAsCause(sb: StringBuilder, | ||
cause: Throwable, | ||
causedTrace: Array[StackTraceElement]): Unit = { | ||
|
||
// Compute number of frames in common between this and caused | ||
val trace = cause.getStackTrace | ||
var m = trace.length - 1 | ||
var n = causedTrace.length - 1 | ||
while (m >= 0 && n >= 0 && trace(m) == causedTrace(n)) { | ||
m -= 1 | ||
n -= 1 | ||
} | ||
|
||
val framesInCommon = trace.length - 1 - m | ||
sb.append("\nCaused by: " + cause) | ||
|
||
for (i <- 0 to m) { | ||
sb.append("\n\tat ").append(trace(i)) | ||
} | ||
|
||
if (framesInCommon != 0) { | ||
sb.append("\n\t... ").append(framesInCommon).append(" more") | ||
} | ||
|
||
// Recurse if we have a cause | ||
val ourCause = cause.getCause | ||
if (ourCause != null) { | ||
printStackTraceAsCause(sb, ourCause, trace) | ||
} | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
redux/src/main/scala/scommons/react/redux/task/TaskManagerUiProps.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package scommons.react.redux.task | ||
|
||
case class TaskManagerUiProps(showLoading: Boolean, | ||
status: Option[String], | ||
onHideStatus: () => Unit, | ||
error: Option[String], | ||
errorDetails: Option[String], | ||
onCloseErrorPopup: () => Unit) |
13 changes: 4 additions & 9 deletions
13
redux/src/test/scala/scommons/react/redux/task/FutureTaskSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.