Simple Board table.

This commit is contained in:
FyloZ 2020-10-08 16:11:29 -04:00
parent ad68d10a3c
commit d6bc50d9f6
28 changed files with 625 additions and 536 deletions

View File

@ -22,10 +22,10 @@ repositories {
dependencies { dependencies {
implementation(platform("org.jetbrains.kotlin:kotlin-bom")) implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.kodein.di:kodein-di:7.0.0") implementation("org.kodein.di:kodein-di:7.1.0")
implementation("org.kodein.di:kodein-di-generic-jvm:6.5.5") implementation("org.kodein.di:kodein-di-generic-jvm:6.5.5")
implementation("org.kodein.di:kodein-di-framework-tornadofx-jvm:7.0.0") implementation("org.kodein.di:kodein-di-framework-tornadofx-jvm:7.1.0")
implementation("io.github.microutils:kotlin-logging:1.5.9") implementation("io.github.microutils:kotlin-logging-jvm:2.0.3")
implementation("org.slf4j:slf4j-simple:1.7.26") implementation("org.slf4j:slf4j-simple:1.7.26")
// implementation("org.dizitart:potassium-nitrite:3.4.2") // implementation("org.dizitart:potassium-nitrite:3.4.2")
implementation("no.tornado:tornadofx:1.7.20") implementation("no.tornado:tornadofx:1.7.20")

View File

@ -1,34 +0,0 @@
package dev.fyloz.plannervio.core.model
import javafx.beans.property.SimpleLongProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import tornadofx.getValue
open class Board(
id: Long,
name: String,
description: String,
backgroundImage: BackgroundImage
) {
val idProperty by lazy { SimpleLongProperty(this, "id", id) }
val id by idProperty
val nameProperty by lazy { SimpleStringProperty(this, "name", name) }
val name by nameProperty
val descriptionProperty by lazy { SimpleStringProperty(this, "description", description) }
val description by descriptionProperty
val backgroundImageProperty by lazy { SimpleObjectProperty(this, "backgroundImage", backgroundImage) }
val backgroundImage by backgroundImageProperty
}
fun boardFactory(
id: Long = 0L,
name: String = "board",
description: String = "description",
backgroundImage: BackgroundImage = backgroundImageFactory()
): Board {
return Board(id, name, description, backgroundImage)
}

View File

@ -0,0 +1,57 @@
package dev.fyloz.plannervio.core.model
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleLongProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import tornadofx.getValue
import tornadofx.setValue
import java.time.LocalDateTime
open class Board(
id: Long,
name: String,
description: String,
remote: Boolean = false,
location: String? = null,
private: Boolean = false,
favorite: Boolean = false,
lastVisited: LocalDateTime? = null
) {
val idProperty by lazy { SimpleLongProperty(this, "id", id) }
var id: Long by idProperty
val nameProperty by lazy { SimpleStringProperty(this, "name", name) }
var name: String by nameProperty
val descriptionProperty by lazy { SimpleStringProperty(this, "description", description) }
var description: String by descriptionProperty
val remoteProperty by lazy { SimpleBooleanProperty(this, "remote", remote) }
var isRemote: Boolean by remoteProperty
val locationProperty by lazy { SimpleStringProperty(this, "location", location) }
var location: String? by locationProperty
val privateProperty by lazy { SimpleBooleanProperty(this, "private", private) }
var isPrivate: Boolean by privateProperty
val favoriteProperty by lazy { SimpleBooleanProperty(this, "favorite", favorite) }
var favorite: Boolean by favoriteProperty
val lastVisitedProperty by lazy { SimpleObjectProperty(this, "lastVisited", lastVisited) }
var lastVisited: LocalDateTime? by lastVisitedProperty
}
/** DSL for [Board]s. */
fun board(
id: Long = 0L,
name: String = "board_name",
description: String = "board_description",
remote: Boolean = false,
location: String? = null,
private: Boolean = false,
favorite: Boolean = false,
lastVisited: LocalDateTime? = null,
op: Board.() -> Unit = {}
) = Board(id, name, description, remote, location, private, favorite, lastVisited).apply(op)

View File

@ -1,32 +0,0 @@
package dev.fyloz.plannervio.core.model
import javafx.beans.property.SimpleListProperty
import javafx.beans.property.SimpleStringProperty
import tornadofx.getValue
import tornadofx.toObservable
class PrivateBoard(
id: Long,
name: String,
description: String,
backgroundImage: BackgroundImage,
password: String,
users: MutableList<User>
) : Board(id, name, description, backgroundImage) {
val passwordProperty = SimpleStringProperty(this, "password", password)
val password by passwordProperty
val usersProperty = SimpleListProperty(this, "users", users.toObservable())
val users by usersProperty
}
fun privateBoardFactory(
id: Long = 0L,
name: String = "private board",
description: String = "description",
backgroundImage: BackgroundImage = backgroundImageFactory(),
password: String = "password",
users: MutableList<User> = mutableListOf()
): PrivateBoard {
return PrivateBoard(id, name, description, backgroundImage, password, users)
}

View File

@ -1,8 +1,16 @@
package dev.fyloz.plannervio.core.model package dev.fyloz.plannervio.core.model
data class User( data class User(
val username: String, var username: String,
val email: String, var email: String,
val password: String var password: String
) { ) {
} }
/** DSL for [User]s. */
fun user(
username: String = "user_username",
email: String = "user_email",
password: String = "user_password",
op: User.() -> Unit = {}
) = User(username, email, password).apply(op)

View File

@ -1,16 +0,0 @@
package dev.fyloz.plannervio.core.model.view
import dev.fyloz.plannervio.core.model.Board
import tornadofx.ItemViewModel
class BoardViewModel(board: Board? = null) : ItemViewModel<Board>() {
init {
if (board != null) item = board
}
val id = bind(Board::idProperty)
val name = bind(Board::nameProperty)
val description = bind(Board::descriptionProperty)
val backgroundImage = bind(Board::backgroundImageProperty)
}

View File

@ -0,0 +1,19 @@
package dev.fyloz.plannervio.core.model.view
import dev.fyloz.plannervio.core.model.Board
import tornadofx.ItemViewModel
open class BoardViewModel(board: Board? = null) : ItemViewModel<Board>() {
init {
if (board != null) item = board
}
val id = bind(Board::idProperty)
val name = bind(Board::nameProperty)
val description = bind(Board::descriptionProperty)
val isRemote = bind(Board::remoteProperty)
val location = bind(Board::locationProperty)
val isPrivate = bind(Board::privateProperty)
val favorite = bind(Board::favoriteProperty)
val lastVisited = bind(Board::lastVisitedProperty)
}

View File

@ -1,80 +1,76 @@
package dev.fyloz.plannervio.core.repository.memory package dev.fyloz.plannervio.core.repository.memory
import dev.fyloz.plannervio.core.model.*
import dev.fyloz.plannervio.core.model.Board import dev.fyloz.plannervio.core.model.Board
import dev.fyloz.plannervio.core.model.board
import dev.fyloz.plannervio.core.repository.IBoardRepository import dev.fyloz.plannervio.core.repository.IBoardRepository
import java.time.LocalDateTime
class MemoryBoardRepository : IBoardRepository { class MemoryBoardRepository : IBoardRepository {
private val boards = mutableListOf<Board>() private val boards = mutableListOf<Board>()
init { init {
save( save(
boardFactory( board {
0L, id = 0L
"Board 1", name = "Board 1"
"Voluptates facere maiores quis suscipit nostrum. Et accusamus quas animi. Similique reiciendis ea iste cum sunt aut minus. Aut cupiditate consequatur dolor vel doloremque aut eius voluptas. Sed reprehenderit veniam odio voluptatem numquam doloribus. Consequuntur esse assumenda aut expedita voluptatibus.", description = "Voluptates facere maiores quis suscipit nostrum. Et accusamus quas animi. Similique reiciendis ea iste cum sunt aut minus. Aut cupiditate consequatur dolor vel doloremque aut eius voluptas. Sed reprehenderit veniam odio voluptatem numquam doloribus. Consequuntur esse assumenda aut expedita voluptatibus."
BackgroundImage("bundled_background_0.jpg") favorite = true
) lastVisited = LocalDateTime.now()
}
) )
save( save(
boardFactory( board {
1L, id = 1L
"test", name = "Board 2"
"Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non.", description = "Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non."
BackgroundImage("bundled_background_1.jpg") }
)
) )
save( save(
privateBoardFactory( board {
2L, id = 2L
"Board 3", name = "Board 3"
"Pariatur error dolores beatae et rem minus a quia. Facilis nam esse repellat consequatur aperiam nostrum aut vel. Non ratione aut maiores beatae molestiae maxime ipsum molestiae. Asperiores est id in quia tempore soluta voluptatem atque.", description = "Pariatur error dolores beatae et rem minus a quia. Facilis nam esse repellat consequatur aperiam nostrum aut vel. Non ratione aut maiores beatae molestiae maxime ipsum molestiae. Asperiores est id in quia tempore soluta voluptatem atque."
BackgroundImage("bundled_background_3.jpg"), isRemote = true
"password", location = "plannervio.fyloz.dev"
mutableListOf( isPrivate = true
User("username", "username@email.com", "password") }
)
)
) )
save( save(
boardFactory( board {
3L, id = 3L
"test", name = "Board 4"
"Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non.", description = "Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non."
BackgroundImage("bundled_background_1.jpg") }
)
) )
save( save(
boardFactory( board {
4L, id = 4L
"test", name = "Board 5"
"Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non.", description = "Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non."
BackgroundImage("bundled_background_1.jpg") }
)
) )
save( save(
boardFactory( board {
5L, id = 5L
"test", name = "Board 6"
"Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non.", description = "Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non."
BackgroundImage("bundled_background_1.jpg") isRemote = true
) location = "todo.google.ca"
}
) )
save( save(
boardFactory( board {
6L, id = 6L
"test", name = "Board 7"
"Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non.", description = "Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non."
BackgroundImage("bundled_background_1.jpg") }
)
) )
save( save(
boardFactory( board {
7L, id = 7L
"test", name = "Board 8"
"Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non.", description = "Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non."
BackgroundImage("bundled_background_1.jpg") }
)
) )
} }

View File

@ -1,52 +0,0 @@
package dev.fyloz.plannervio.core.service
import dev.fyloz.plannervio.core.model.BackgroundImage
import javafx.scene.image.Image
import mu.KotlinLogging
import java.io.FileInputStream
import java.io.InputStream
class BackgroundImageService : IBackgroundImageService {
private val logger = KotlinLogging.logger { }
override fun loadImage(image: BackgroundImage): Image? {
val stream = getImageInputStream(image)
return if (stream != null)
Image(stream)
else {
logger.warn("Could not load image '${image.path}'")
null
}
}
/**
* Gets the input stream for a given background image.
*
* @param image The background image
*/
fun getImageInputStream(image: BackgroundImage): InputStream? {
return if (image.bundled) {
getBundledImageInputStream(image)
} else {
getCustomImageInputStream(image)
}
}
/**
* Gets the input stream for a given bundled background image.
*
* @param image The background image
*/
fun getBundledImageInputStream(image: BackgroundImage): InputStream? {
return ClassLoader.getSystemResourceAsStream("images/${image.path}")
}
/**
* Gets the input stream for a given custom background image.
*
* @param image The background image
*/
fun getCustomImageInputStream(image: BackgroundImage): InputStream? {
return FileInputStream(image.path)
}
}

View File

@ -5,11 +5,16 @@ import dev.fyloz.plannervio.core.model.Board
import org.kodein.di.DI import org.kodein.di.DI
import org.kodein.di.instance import org.kodein.di.instance
class BoardService(di: DI) : IBoardService { /** A service for boards. */
override val repository: IBoardRepository by di.instance() interface IBoardService {
/** Gets an immutable list containing all boards. */
fun getBoards(): List<Board>
}
class BoardServiceImpl(di: DI) : IBoardService {
private val repository: IBoardRepository by di.instance()
override fun getBoards(): List<Board> { override fun getBoards(): List<Board> {
return repository.findAll() return repository.findAll()
} }
} }

View File

@ -28,7 +28,7 @@ private const val keywordsServiceKey = "KEYWORDS"
private val keywordsBundle: ResourceBundle = getBundle(keywordsServiceKey.toLowerCase()) private val keywordsBundle: ResourceBundle = getBundle(keywordsServiceKey.toLowerCase())
private val i18nServices: MutableMap<String, I18nService> = mutableMapOf( private val i18nServices: MutableMap<String, I18nService> = mutableMapOf(
keywordsServiceKey to I18nService(null, keywordsBundle) // keywordsServiceKey to I18nService(null, keywordsBundle)
) )
/** /**

View File

@ -1,21 +0,0 @@
package dev.fyloz.plannervio.core.service
import dev.fyloz.plannervio.core.model.BackgroundImage
import javafx.scene.image.Image
/**
* A service for background images.
*/
interface IBackgroundImageService {
/**
* Load the JavaFX image for a given BackgroundImage.
*
* If the file cannot be loaded, null will be returned.
*
* @param image The image to load
* @return The JavaFX image for the BackgroundImage
*/
fun loadImage(image: BackgroundImage): Image?
}

View File

@ -1,19 +0,0 @@
package dev.fyloz.plannervio.core.service
import dev.fyloz.plannervio.core.repository.IBoardRepository
import dev.fyloz.plannervio.core.model.Board
/**
* A service for boards.
*/
interface IBoardService {
val repository: IBoardRepository
/**
* Get all boards.
*
* @return An immutable list containing all boards
*/
fun getBoards(): List<Board>
}

View File

@ -1,41 +0,0 @@
package dev.fyloz.plannervio.core.service
import com.beust.klaxon.Klaxon
import com.jfoenix.svg.SVGGlyph
import javafx.scene.paint.Color
import mu.KotlinLogging
import tornadofx.SVGIcon
private val logger = KotlinLogging.logger { }
private val storedSvgPaths: List<SvgPath> = loadSvgPaths()
fun svgGlyph(pathName: SvgPathName, size: Double = 16.0, color: Color = Color.BLACK): SVGGlyph? {
val svgPath = svgPath(pathName) ?: return null
val glyph = SVGGlyph(svgPath.path, color)
glyph.setSize(size, size)
return glyph
}
private fun loadSvgPaths(): List<SvgPath> {
val doc = SvgPath::class.java.getResource("/images/icons.json").readText()
return Klaxon().parseArray(doc) ?: throw IllegalStateException("Could not load svg paths.")
}
private fun svgPath(pathName: SvgPathName): SvgPath? {
val svgPath = storedSvgPaths.firstOrNull {
pathName.pathName == it.name
}
if (svgPath == null) {
logger.warn("Svg path ${pathName.pathName} not loaded or not found")
}
return svgPath
}
private data class SvgPath(val name: String, val path: String)
enum class SvgPathName(val pathName: String) {
FORMAT_LIST_BULLETED("format_list_bulleted"),
STAR("star"),
HISTORY("history"),
COG("cog")
}

View File

@ -2,12 +2,12 @@ package dev.fyloz.plannervio.ui
import dev.fyloz.plannervio.core.repository.IBoardRepository import dev.fyloz.plannervio.core.repository.IBoardRepository
import dev.fyloz.plannervio.core.repository.memory.MemoryBoardRepository import dev.fyloz.plannervio.core.repository.memory.MemoryBoardRepository
import dev.fyloz.plannervio.core.service.BackgroundImageService import dev.fyloz.plannervio.core.service.BoardServiceImpl
import dev.fyloz.plannervio.core.service.BoardService
import dev.fyloz.plannervio.core.service.IBackgroundImageService
import dev.fyloz.plannervio.core.service.IBoardService import dev.fyloz.plannervio.core.service.IBoardService
import dev.fyloz.plannervio.ui.style.Style import dev.fyloz.plannervio.ui.style.Style
import dev.fyloz.plannervio.ui.view.MainView import dev.fyloz.plannervio.ui.view.MainView
import mu.KLogger
import mu.KotlinLogging
import org.kodein.di.DI import org.kodein.di.DI
import org.kodein.di.DIAware import org.kodein.di.DIAware
import org.kodein.di.bind import org.kodein.di.bind
@ -22,14 +22,18 @@ fun main(args: Array<String>) {
launch<Plannervio>(args) launch<Plannervio>(args)
} }
val logger: KLogger by lazy {
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "trace")
KotlinLogging.logger(Plannervio::class.simpleName!!)
}
class Plannervio : App(MainView::class, Style::class), DIAware { class Plannervio : App(MainView::class, Style::class), DIAware {
override val di: DI override val di: DI
get() = DI { get() = DI {
installTornadoSource() installTornadoSource()
bind<IBoardRepository>() with singleton { MemoryBoardRepository() } bind<IBoardRepository>() with singleton { MemoryBoardRepository() }
bind<IBoardService>() with singleton { BoardService(di) } bind<IBoardService>() with singleton { BoardServiceImpl(di) }
bind<IBackgroundImageService>() with singleton { BackgroundImageService() }
} }
init { init {

View File

@ -8,28 +8,16 @@ import java.util.*
* @param initBundles The bundles contained by the merged bundle * @param initBundles The bundles contained by the merged bundle
*/ */
class MergedResourceBundle(vararg initBundles: ResourceBundle) : ResourceBundle() { class MergedResourceBundle(vararg initBundles: ResourceBundle) : ResourceBundle() {
private val bundles: MutableList<ResourceBundle> private val bundles: MutableList<ResourceBundle> = initBundles.toMutableList()
init { /** Adds a [bundle] to the contained bundles. */
bundles = initBundles.toMutableList()
}
/**
* Adds a bundle to contained bundles.
*
* @param bundle The bundle to add
*/
operator fun plus(bundle: ResourceBundle) { operator fun plus(bundle: ResourceBundle) {
bundles + bundle bundles += bundle
} }
/** /** Adds [bundles] to the contained bundles. */
* Removes a bundle from contained bundles. operator fun plus(bundles: Iterable<ResourceBundle>) {
* this.bundles += bundles
* @param bundle The bundle to remove
*/
operator fun minus(bundle: ResourceBundle) {
bundles - bundle
} }
override fun handleGetObject(key: String): Any? { override fun handleGetObject(key: String): Any? {

View File

@ -1,39 +1,45 @@
package dev.fyloz.plannervio.ui.style package dev.fyloz.plannervio.ui.style
import javafx.geometry.Pos
import javafx.scene.Cursor import javafx.scene.Cursor
import javafx.scene.paint.Color import javafx.scene.paint.Color
import javafx.scene.paint.Paint import javafx.scene.paint.Paint
import javafx.scene.text.TextAlignment
import kfoenix.JFXStylesheet.Companion.jfxButton import kfoenix.JFXStylesheet.Companion.jfxButton
import kfoenix.JFXStylesheet.Companion.jfxListView
import kfoenix.JFXStylesheet.Companion.jfxRippler import kfoenix.JFXStylesheet.Companion.jfxRippler
import kfoenix.JFXStylesheet.Companion.jfxRipplerFill import kfoenix.JFXStylesheet.Companion.jfxRipplerFill
import kfoenix.JFXStylesheet.Companion.jfxVerticalGap
import tornadofx.* import tornadofx.*
val primaryTheme = mapOf( val primaryTheme = mapOf(
50 to c("#fcf2e7"), 50 to c("#fcf2e7"),
100 to c("#f8dec3"), 100 to c("#f8dec3"),
200 to c("#f3c89c"), 200 to c("#f3c89c"),
300 to c("#eeb274"), 300 to c("#eeb274"),
400 to c("#eaa256"), 400 to c("#eaa256"),
500 to c("#e69138"), 500 to c("#e69138"),
600 to c("#e38932"), 600 to c("#e38932"),
700 to c("#df7e2b"), 700 to c("#df7e2b"),
800 to c("#db7424"), 800 to c("#db7424"),
900 to c("#d56217") 900 to c("#d56217")
) )
val accentTheme = mapOf( val accentTheme = mapOf(
50 to c("#e8f0f8"), 50 to c("#e8f0f8"),
100 to c("#c5daee"), 100 to c("#c5daee"),
200 to c("#9ec2e3"), 200 to c("#9ec2e3"),
300 to c("#77aad7"), 300 to c("#77aad7"),
400 to c("#5a97cf"), 400 to c("#5a97cf"),
500 to c("#3d85c6"), 500 to c("#3d85c6"),
600 to c("#377dc0"), 600 to c("#377dc0"),
700 to c("#2f72b9"), 700 to c("#2f72b9"),
800 to c("#2768b1"), 800 to c("#2768b1"),
900 to c("#1a55a4") 900 to c("#1a55a4")
) )
val background = Color.WHITE
class Style : Stylesheet() { class Style : Stylesheet() {
companion object { companion object {
@ -47,8 +53,10 @@ class Style : Stylesheet() {
// Classes // Classes
val active by cssclass() val active by cssclass()
val appNameLabel by cssclass() val appNameLabel by cssclass()
val navBar by cssclass() val centered by cssclass()
val jfxSvgGlyphWrapper by cssclass() val jfxSvgGlyphWrapper by cssclass()
val navBar by cssclass()
val test by cssclass()
} }
init { init {
@ -100,5 +108,44 @@ class Style : Stylesheet() {
} }
} }
} }
tableView {
backgroundColor += background
borderColor += box(c(0.0, 0.0, 0.0, 0.12))
columnHeader {
backgroundColor += background
prefHeight = 56.px
label {
alignment = Pos.CENTER_LEFT
}
}
filler {
backgroundColor += background
}
tableRowCell {
prefHeight = 52.px
borderColor += box(c(0.0, 0.0, 0.0, 0.12), background, background, background)
borderWidth += box(1.px, 0.px, 0.px, 0.px)
backgroundColor += background
and(hover) {
backgroundColor += c(0.0, 0.0, 0.0, 0.04)
}
}
tableColumn {
alignment = Pos.CENTER_LEFT
borderColor += box(Color.TRANSPARENT)
padding = box(0.px, 16.px)
and(centered) {
alignment = Pos.CENTER
}
}
}
} }
} }

View File

@ -1,18 +1,34 @@
package dev.fyloz.plannervio.ui.view package dev.fyloz.plannervio.ui.view
import com.jfoenix.controls.JFXButton import dev.fyloz.plannervio.core.model.Board
import com.jfoenix.controls.JFXTextField import dev.fyloz.plannervio.core.service.IBoardService
import dev.fyloz.plannervio.core.service.SvgPathName
import dev.fyloz.plannervio.ui.style.Style import dev.fyloz.plannervio.ui.style.Style
import javafx.beans.property.SimpleBooleanProperty import javafx.application.Platform
import javafx.beans.property.SimpleObjectProperty import javafx.beans.binding.Bindings
import javafx.geometry.Pos import javafx.geometry.Pos
import javafx.scene.control.Label
import javafx.scene.layout.HBox
import javafx.scene.layout.Priority import javafx.scene.layout.Priority
import javafx.scene.paint.Color
import kfoenix.jfxtextfield
import org.kodein.di.instance
import org.kodein.di.tornadofx.kodeinDI
import tornadofx.* import tornadofx.*
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
/** The app's main view */ /** The app's main view */
class MainView : AbstractView() { class MainView : View() {
private val presenter: MainViewPresenter by inject() private val presenter: MainViewPresenter by inject()
private var activeTabView: View = presenter.activeView
set(value) {
activeTabView.replaceWith(value)
field = value
}
init {
messages = messages("lang.views.main")
}
override val root = borderpane { override val root = borderpane {
left = vbox { left = vbox {
@ -24,24 +40,153 @@ class MainView : AbstractView() {
alignment = Pos.CENTER alignment = Pos.CENTER
text = "Plannervio" text = "Plannervio"
} }
this += JFXTextField().apply { this += jfxtextfield {
vgrow = Priority.NEVER vgrow = Priority.NEVER
isLabelFloat = true isLabelFloat = true
promptText = messages["search"] // TODO promptText = messages[MainViewKeywords.NAV_SEARCH_LABEL]
vboxMargin(horizontal = 16, vertical = 20) vboxMargin(horizontal = 16, vertical = 20)
} }
this += tabButton(Tab.BOARDS, presenter::onTabButtonClick).apply { for (t in Tab.values()) {
presenter.switchTab(this) // Default tab this += tabButton {
tab = t
alignment = Pos.BOTTOM_LEFT
textFill = Color.RED
graphic = hbox {
paddingTop = 2
this += pane {
hboxMargin(top = 8)
addClass(Style.jfxSvgGlyphWrapper)
this += svgGlyph(t.icon, size = 16.0, color = Color.WHITE)
}
this += Label(messages[t.messageKey])
}
setOnMouseClicked { presenter.onTabButtonClick(this) }
}
} }
this += tabButton(Tab.FAVORITES, presenter::onTabButtonClick)
this += tabButton(Tab.HISTORY, presenter::onTabButtonClick)
this += tabButton(Tab.SETTINGS, presenter::onTabButtonClick)
} }
center = vbox {
isFillWidth = true
padding = insets(10.0)
this += activeTabView
}
}
fun resetCurrentTab() {
val view = presenter.activeView
activeTabView = view
}
}
class BoardsView : View() {
private val presenter by inject<BoardsTabViewPresenter>()
private val headerRowHeightRatio = 1.15
private val rowHeight = 52.0
init {
messages = messages("lang.views.main")
}
private val defaultBoardLocation = messages[Keywords.BOARD_LOCATION_LOCAL]
override val root = tableview(presenter.boards.toObservable()) {
fixedCellSize = rowHeight
prefHeightProperty().bind(fixedCellSizeProperty() * (Bindings.size(items) + headerRowHeightRatio))
columns.setAll(
column(messages[Keywords.BOARD_NAME], String::class) {
minWidth = 150.0
value { it.value.name }
},
column(messages[Keywords.BOARD_LOCATION], String::class) {
value { it.value.location ?: defaultBoardLocation }
minWidth = 150.0
setComparator { a, b ->
when {
a != defaultBoardLocation && b == defaultBoardLocation -> 1
a == defaultBoardLocation && b != defaultBoardLocation -> -1
a == defaultBoardLocation && b == defaultBoardLocation -> 0
else -> a.compareTo(b) * -1
}
}
},
column(messages[Keywords.BOARD_PROTECTION], HBox::class) {
fixedWidth(120.0)
value {
if (it.value.isRemote) {
val private = it.value.isPrivate
LabeledIcon(
if (private) SvgIconName.LOCK else SvgIconName.EARTH,
if (private) messages[Keywords.BOARD_PROTECTION_PRIVATE] else messages[Keywords.BOARD_PROTECTION_PUBLIC]
)
} else null
}
},
column(messages[Keywords.BOARD_FAVORITE], ToggleableIconButton::class) {
fixedWidth(100.0)
value {
toggleableIconButton(SvgIconName.STAR, Color.GOLD, Color.GREY) {
enabled = it.value.favorite
}
}
},
column(messages[Keywords.BOARD_LAST_VISITED], String::class) {
value { it.value.lastVisited?.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)) }
fixedWidth(150.0)
}
)
// Prevent selection
selectionModel.selectedIndexProperty().addListener { _, _, _ -> Platform.runLater { selectionModel.clearSelection() } }
}
}
class FavoritesView : View() {
override val root = pane {
this += Label("FAVORITES")
}
}
class RecentsView : View() {
override val root = pane {
this += Label("RECENTS")
}
}
class SettingsView : View() {
override val root = pane {
this += Label("SETTINGS")
} }
} }
class MainViewPresenter : Controller() { class MainViewPresenter : Controller() {
private var activeTabButton: TabButton? = null private val view: MainView by inject()
private val boardSelectionView = find<BoardsView>()
private val favoritesView = find<FavoritesView>()
private val recentView = find<RecentsView>()
private val settingsView = find<SettingsView>()
private val defaultActiveView = boardSelectionView
/** The currently active [TabButton]. **/
var activeTabButton: TabButton? = null
private set
/** The currently active [Tab]. */
private val activeTab: Tab?
get() = activeTabButton?.tab
/** The currently active view. */
val activeView: View
get() {
val tab = activeTab
return if (tab == null) defaultActiveView else when (tab) {
Tab.BOARDS -> boardSelectionView
Tab.SETTINGS -> settingsView
Tab.FAVORITES -> favoritesView
Tab.HISTORY -> recentView
}
}
/** Called when a [TabButton] has been clicked. */ /** Called when a [TabButton] has been clicked. */
fun onTabButtonClick(button: TabButton) { fun onTabButtonClick(button: TabButton) {
@ -54,32 +199,38 @@ class MainViewPresenter : Controller() {
} }
/** Switch the active tab to [button]'s tab. */ /** Switch the active tab to [button]'s tab. */
fun switchTab(button: TabButton) { private fun switchTab(button: TabButton) {
activeTabButton?.active = false activeTabButton?.active = false
button.active = true button.active = true
activeTabButton = button activeTabButton = button
view.resetCurrentTab()
} }
} }
/** The [TabButton] is a button linked to a [tab]. */ abstract class TabViewPresenter : Controller() {
class TabButton(tab: Tab, active: Boolean = false) : JFXButton() { protected abstract val view: View
/** Specifies the [Tab] linked to the button. */ protected val boardService: IBoardService by kodeinDI().instance()
val tabProperty by lazy { SimpleObjectProperty(tab) }
var tab: Tab by tabProperty
/** Specifies if the button is active. */
val activeProperty = SimpleBooleanProperty(this, "active", active)
var active: Boolean
get() = activeProperty.get()
set(value) {
toggleClass(Style.active, value)
activeProperty.set(value)
}
} }
enum class Tab(val icon: SvgPathName, val i18nText: String) { class BoardsTabViewPresenter : TabViewPresenter() {
BOARDS(SvgPathName.FORMAT_LIST_BULLETED, "tab.boards.label"), override val view: BoardsView by inject()
FAVORITES(SvgPathName.STAR, "tab.favorites.label"),
HISTORY(SvgPathName.HISTORY, "tab.history.label"), val boards: List<Board>
SETTINGS(SvgPathName.COG, "tab.settings.label") get() = boardService.getBoards()
}
object MainViewKeywords {
const val NAV_SEARCH_LABEL = "nav.search.label"
const val NAV_TAB_BOARDS_LABEL = "nav.tab.boards.label"
const val NAV_TAB_FAVORITES_LABEL = "nav.tab.favorites.label"
const val NAV_TAB_HISTORY_LABEL = "nav.tab.history.label"
const val NAV_TAB_SETTINGS_LABEL = "nav.tab.settings.label"
const val BOARDS_VIEW_OPEN_LABEL = "view.boards.open.label"
}
enum class Tab(val icon: SvgIconName, val messageKey: String) {
BOARDS(SvgIconName.FORMAT_LIST_BULLETED, MainViewKeywords.NAV_TAB_BOARDS_LABEL),
FAVORITES(SvgIconName.STAR, MainViewKeywords.NAV_TAB_FAVORITES_LABEL),
HISTORY(SvgIconName.HISTORY, MainViewKeywords.NAV_TAB_HISTORY_LABEL),
SETTINGS(SvgIconName.COG, MainViewKeywords.NAV_TAB_SETTINGS_LABEL)
} }

View File

@ -1,105 +1,210 @@
package dev.fyloz.plannervio.ui.view package dev.fyloz.plannervio.ui.view
import dev.fyloz.plannervio.core.service.locateI18nService import com.beust.klaxon.Klaxon
import dev.fyloz.plannervio.core.service.svgGlyph import com.jfoenix.controls.JFXButton
import com.jfoenix.svg.SVGGlyph
import dev.fyloz.plannervio.ui.Plannervio
import dev.fyloz.plannervio.ui.i18n.MergedResourceBundle
import dev.fyloz.plannervio.ui.logger
import dev.fyloz.plannervio.ui.style.Style import dev.fyloz.plannervio.ui.style.Style
import javafx.beans.property.ReadOnlyStringWrapper
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import javafx.event.EventTarget
import javafx.geometry.Pos import javafx.geometry.Pos
import javafx.scene.control.Label import javafx.scene.Node
import javafx.scene.layout.HBox import javafx.scene.layout.HBox
import javafx.scene.layout.Pane
import javafx.scene.layout.Region
import javafx.scene.layout.VBox import javafx.scene.layout.VBox
import javafx.scene.paint.Color import javafx.scene.paint.Color
import kfoenix.JFXStylesheet.Companion.jfxRipplerFill
import kfoenix.jfxrippler
import tornadofx.* import tornadofx.*
import java.util.*
/** const val KEYWORDS_BUNDLE_NAME = "lang.keywords"
* A view. Inheriting this class allows to automatically link the FXML file and the CSS files for the current theme to the fragment.
* If a presenter class is provided, the presenter will also be created.
*
* @constructor Initialize the view, linking the FXML and the CSS files.
*/
abstract class AbstractView : View() {
/**
* Gets the name of the view's files in kebab-case.
*/
val filename by lazy {
javaClass.simpleName
.split("(?=[A-Z])".toRegex())
.map { it.toLowerCase() }
.filter { it.isNotEmpty() }
.filter { it != "view" }
.joinToString("-")
}
protected val i18nService = locateI18nService(filename) /** A SVG icon. */
data class SvgIcon(val name: String, val author: String, val path: String)
init { /** The path to the file containing svg icons. */
messages = i18nService.bundle private const val SVG_ICONS_FILE_PATH = "/images/icons.json"
}
// final override val root: Pane = loadFXML("/fxml/views/${filename}.fxml") /** A [List] containing all available [SvgIcon]s. */
val svgIcons = loadSvgIcons()
/** Load all [SvgIcon]s from the file at the path specified by [SVG_ICONS_FILE_PATH]. */
private fun loadSvgIcons(): List<SvgIcon> {
val doc = Plannervio::class.java.getResource(SVG_ICONS_FILE_PATH).readText()
val allIcons: List<SvgIcon> = Klaxon().parseArray(doc) ?: throw IllegalStateException("Could not load SVG icons.")
logger.debug("Loaded ${allIcons.size} SVG icons.")
return allIcons
} }
/** /** Create a [SVGGlyph] for the given [svgIconName] with the given [size] and [color]. */
* A fragment of the view. Inheriting this class allows to automatically link the FXML file and the CSS files for the current theme to the fragment. fun svgGlyph(svgIconName: SvgIconName, size: Double = 16.0, color: Color = Color.BLACK): SVGGlyph {
* val svgIcon = svgIcons.firstOrNull { svgIconName.iconName == it.name }
* @property view The view which the fragment is linked to. ?: throw IllegalStateException("Cannot create a SVGGlyph for the icon ${svgIconName.iconName} because no icon loaded was found with this name.")
* @constructor Initialize the fragment, linking the FXML and the CSS files. val glyph = SVGGlyph(svgIcon.path, color)
*/ glyph.size = size
abstract class AbstractFragment(val view: AbstractView? = null) : Fragment() { return glyph
protected val i18nService = locateI18nService(view?.filename)
init {
messages = i18nService.bundle
}
} }
/** Shorthand to create a [TabButton] in a [View]. */ /** A button linked to a [tab]. */
fun View.tabButton(tab: Tab, onClick: (TabButton) -> Unit): TabButton { class TabButton(tab: Tab, active: Boolean = false) : JFXButton() {
return TabButton(tab).apply { /** The [Tab] linked to the button. */
alignment = Pos.BOTTOM_LEFT val tabProperty by lazy { SimpleObjectProperty(tab) }
textFill = Color.RED var tab: Tab by tabProperty
graphic = hbox {
paddingTop = 2 /** If the button is active. */
this += pane { val activeProperty = SimpleBooleanProperty(this, "active", active)
hboxMargin(top = 8) var active: Boolean
addClass(Style.jfxSvgGlyphWrapper) get() = activeProperty.get()
this += svgGlyph(tab.icon, size = 16.0, color = Color.WHITE)!! set(value) {
} toggleClass(Style.active, value)
this += Label(messages[tab.i18nText]) activeProperty.set(value)
}
}
/** A [SvgIcon] and a [Label] wrapped in a [HBox]. */
class LabeledIcon(svgIconName: SvgIconName, text: String, iconSize: Double = 16.0, iconColor: Color = Color.BLACK) : HBox() {
/** The name of the [SvgIcon]. */
val iconNameProperty by lazy { ReadOnlyStringWrapper(svgIconName.iconName) }
val iconName: String by iconNameProperty
/** The text of the [Label]. */
val textProperty by lazy { SimpleStringProperty(text) }
var text: String by textProperty
override fun getUserAgentStylesheet(): String = LabeledIconStyle().base64URL.toExternalForm()
init {
addClass(LabeledIconStyle.labeledIcon)
children.setAll(
svgGlyph(svgIconName, iconSize, iconColor),
label(textProperty)
)
}
private class LabeledIconStyle : Stylesheet() {
companion object {
val labeledIcon by cssclass()
}
init {
labeledIcon {
alignment = Pos.CENTER_LEFT
label {
padding = box(0.px, 0.px, 0.px, 10.px)
}
}
} }
setOnMouseClicked { onClick(this) }
} }
} }
/** Sets [all] margins of a [Region] contained in a [VBox] to the given size. */ /** A toggleable icon [JFXButton], with an [enabledColor] and a [disabledColor]. */
fun Region.vboxMargin(all: Number = 0) { class ToggleableIconButton(svgIconName: SvgIconName, enabledColor: Color, disabledColor: Color, backgroundColor: Color = Color.WHITE, size: Double = 16.0, enabled: Boolean = false) : JFXButton() {
val enabledProperty by lazy { SimpleBooleanProperty(enabled) }
var enabled by enabledProperty
val enabledColorProperty by lazy { SimpleObjectProperty(enabledColor) }
val enabledColor: Color by enabledColorProperty
val disabledColorProperty by lazy { SimpleObjectProperty(disabledColor) }
val disabledColor: Color by disabledColorProperty
private val svgIcon: SVGGlyph
private val iconColor: Color
get() = if (enabled) enabledColor else disabledColor
init {
svgIcon = svgGlyph(svgIconName, size, color = iconColor)
graphic = svgIcon
ripplerFill = backgroundColor
enabledProperty.onChange {
this.enabled = it
toggle()
}
setOnMouseClicked {
this.enabled = !this.enabled
toggle()
}
}
private fun toggle() {
svgIcon.fill = iconColor
}
}
/** DSL for [TabButton]s. */
fun EventTarget.tabButton(tab: Tab = Tab.BOARDS, op: TabButton.() -> Unit = {}) = TabButton(tab).attachTo(this, op)
/** DSL for [ToggleableIconButton]s. */
fun EventTarget.toggleableIconButton(
svgIconName: SvgIconName,
enabledColor: Color,
disabledColor: Color,
backgroundColor: Color = Color.WHITE,
size: Double = 16.0,
enabled: Boolean = false,
op: ToggleableIconButton.() -> Unit = {}) =
ToggleableIconButton(svgIconName, enabledColor, disabledColor, backgroundColor, size, enabled).attachTo(this, op)
fun messages(vararg names: String): ResourceBundle {
val bundle = MergedResourceBundle(ResourceBundle.getBundle(KEYWORDS_BUNDLE_NAME))
bundle + names.map { ResourceBundle.getBundle(it) }
return bundle
}
/** Sets [all] margins of a [Node] contained in a [VBox] to the given size. */
fun Node.vboxMargin(all: Number = 0) {
this.vboxMargin(all, all) this.vboxMargin(all, all)
} }
/** Sets [horizontal] and [vertical] margins of a [Region] contained in a [VBox] to the given size. */ /** Sets [horizontal] and [vertical] margins of a [Node] contained in a [VBox] to the given size. */
fun Region.vboxMargin(horizontal: Number = 0, vertical: Number = 0) { fun Node.vboxMargin(horizontal: Number = 0, vertical: Number = 0) {
this.vboxMargin(vertical, horizontal, vertical, horizontal) this.vboxMargin(vertical, horizontal, vertical, horizontal)
} }
/** Sets [top], [right], [bottom] and [left] margins of a [Region] contained in a [VBox] to the given size. */ /** Sets [top], [right], [bottom] and [left] margins of a [Node] contained in a [VBox] to the given size. */
fun Region.vboxMargin(top: Number = 0, right: Number = 0, bottom: Number = 0, left: Number = 0) { fun Node.vboxMargin(top: Number = 0, right: Number = 0, bottom: Number = 0, left: Number = 0) {
VBox.setMargin(this, insets(top.toDouble(), right.toDouble(), bottom.toDouble(), left.toDouble())) VBox.setMargin(this, insets(top.toDouble(), right.toDouble(), bottom.toDouble(), left.toDouble()))
} }
/** Sets [all] margins of a [Region] contained in a [HBox] to the given size. */ /** Sets [all] margins of a [Node] contained in a [HBox] to the given size. */
fun Region.hboxMargin(all: Number = 0) { fun Node.hboxMargin(all: Number = 0) {
this.hboxMargin(all, all) this.hboxMargin(all, all)
} }
/** Sets [horizontal] and [vertical] margins of a [Region] contained in a [HBox] to the given size. */ /** Sets [horizontal] and [vertical] margins of a [Node] contained in a [HBox] to the given size. */
fun Region.hboxMargin(horizontal: Number = 0, vertical: Number = 0) { fun Node.hboxMargin(horizontal: Number = 0, vertical: Number = 0) {
this.hboxMargin(vertical, horizontal, vertical, horizontal) this.hboxMargin(vertical, horizontal, vertical, horizontal)
} }
/** Sets [top], [right], [bottom] and [left] margins of a [Region] contained in a [HBox] to the given size. */ /** Sets [top], [right], [bottom] and [left] margins of a [Node] contained in a [HBox] to the given size. */
fun Region.hboxMargin(top: Number = 0, right: Number = 0, bottom: Number = 0, left: Number = 0) { fun Node.hboxMargin(top: Number = 0, right: Number = 0, bottom: Number = 0, left: Number = 0) {
HBox.setMargin(this, insets(top.toDouble(), right.toDouble(), bottom.toDouble(), left.toDouble())) HBox.setMargin(this, insets(top.toDouble(), right.toDouble(), bottom.toDouble(), left.toDouble()))
} }
object Keywords {
const val BOARD_NAME = "board.name"
const val BOARD_DESCRIPTION = "board.description"
const val BOARD_LAST_VISITED = "board.lastVisited"
const val BOARD_FAVORITE = "board.favorite"
const val BOARD_PROTECTION = "board.protection"
const val BOARD_PROTECTION_PRIVATE = "board.protection.private"
const val BOARD_PROTECTION_PUBLIC = "board.protection.public"
const val BOARD_LOCATION = "board.location"
const val BOARD_LOCATION_LOCAL = "board.location.local"
}
/** An enum containing the name of used [SvgIcon]s, for easy access. Not all available icons are specified in this enum. */
enum class SvgIconName(val iconName: String) {
COG("cog"),
EARTH("earth"),
FORMAT_LIST_BULLETED("format-list-bulleted"),
HISTORY("history"),
LOCK("lock"),
STAR("star")
}

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,9 @@
members={0} Members board.name=Name
private=Private board.description=Description
public=Public board.lastVisited=Last visit
name=Name board.favorite=Favorite
description=Description board.protection=Protection
open=OPEN board.location.local=This computer
search=Search... board.location=Location
board.protection.private=Private
board.protection.public=Public

View File

@ -1,7 +1,9 @@
private=Privé board.name=Nom
public=Publique board.description=Description
members={0} Membres board.lastVisited=Dernière visite
name=Nom board.favorite=Favoris
description=Description board.protection=Protection
open=OUVRIR board.location.local=Cet ordinateur
search=Recherche... board.location=Emplacement
board.protection.private=Privé
board.protection.public=Publique

View File

@ -1,4 +0,0 @@
tab.boards.label=Boards
tab.favorites.label=Favorites
tab.history.label=Recents
tab.settings.label=Settings

View File

@ -1,4 +0,0 @@
tab.boards.label=Tableaux
tab.favorites.label=Favoris
tab.history.label=Récents
tab.settings.label=Paramètres

View File

@ -0,0 +1,6 @@
nav.search.label=Search...
nav.tab.boards.label=Boards
nav.tab.favorites.label=Favorites
nav.tab.history.label=Recents
nav.tab.settings.label=Settings
view.boards.open.label=Open

View File

@ -0,0 +1,6 @@
nav.search.label=Recherche...
nav.tab.boards.label=Tableaux
nav.tab.favorites.label=Favoris
nav.tab.history.label=Récents
nav.tab.settings.label=Paramètres
view.boards.open.label=Ouvrir

View File

@ -1,67 +0,0 @@
package dev.fyloz.plannervio.core.service
import dev.fyloz.plannervio.core.model.BackgroundImage
import io.mockk.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import java.io.InputStream
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
class BackgroundImageServiceTest {
private val service: BackgroundImageService = spyk()
private val fakeInputStream: InputStream = mockk()
private val backgroundImage = BackgroundImage("image", true)
@BeforeEach
internal fun setUp() {
clearAllMocks()
}
@Nested
inner class LoadImage {
@Test
fun `Image is loaded`() {
every { service.getImageInputStream(backgroundImage) } returns fakeInputStream
val found = service.loadImage(backgroundImage)
assertNotNull(found)
}
@Test
fun `Image is null`() {
every { service.getImageInputStream(backgroundImage) } returns null
val found = service.loadImage(backgroundImage)
assertNull(found)
}
}
@Nested
inner class GetImageInputStream {
@Test
fun `call getBundledImageInputStream when bundled`() {
every { service.getBundledImageInputStream(backgroundImage) } returns fakeInputStream
val found = service.getImageInputStream(backgroundImage)
verify(exactly = 1) { service.getBundledImageInputStream(backgroundImage) }
assertEquals(fakeInputStream, found)
}
@Test
fun `call getCustomImageInputStream when custom`() {
val customBackgroundImage = BackgroundImage("image", false)
every { service.getCustomImageInputStream(customBackgroundImage) } returns fakeInputStream
val found = service.getImageInputStream(customBackgroundImage)
verify(exactly = 1) { service.getCustomImageInputStream(customBackgroundImage) }
assertEquals(fakeInputStream, found)
}
}
}

View File

@ -1,7 +1,7 @@
package dev.fyloz.plannervio.core.service package dev.fyloz.plannervio.core.service
import dev.fyloz.plannervio.core.model.Board import dev.fyloz.plannervio.core.model.Board
import dev.fyloz.plannervio.core.model.boardFactory import dev.fyloz.plannervio.core.model.board
import dev.fyloz.plannervio.core.repository.IBoardRepository import dev.fyloz.plannervio.core.repository.IBoardRepository
import io.mockk.clearAllMocks import io.mockk.clearAllMocks
import io.mockk.every import io.mockk.every
@ -15,13 +15,13 @@ import org.kodein.di.singleton
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue import kotlin.test.assertTrue
class BoardServiceTest { class BoardServiceImplTest {
private val di: DI = DI { private val di: DI = DI {
bind<IBoardRepository>() with singleton { repository } bind<IBoardRepository>() with singleton { repository }
} }
private val repository: IBoardRepository = mockk() private val repository: IBoardRepository = mockk()
private val service: IBoardService = BoardService(di) private val service: IBoardService = BoardServiceImpl(di)
@BeforeEach @BeforeEach
fun setUp() { fun setUp() {
@ -33,8 +33,8 @@ class BoardServiceTest {
@Test @Test
fun `all boards are included`() { fun `all boards are included`() {
val boards: List<Board> = listOf( val boards: List<Board> = listOf(
boardFactory(0L), board(0L),
boardFactory(1L) board(1L)
) )
every { repository.findAll() } returns boards every { repository.findAll() } returns boards