Stage 5 Changes - Xantier/multiplat-workshop GitHub Wiki

Implementing client logic

Common Client

  • CommonPhotoRenderer *
expect object PhotoRenderer{
    fun render(photos: List<Photo>)
}
  • CommonPhotoProvider
    • Modify logic to get photos and call render function from object

Optional

JS

  • build.gradle
    • Modify JS compile options to output commonjs modules
compileKotlin2Js.kotlinOptions.moduleKind = "commonjs"

Client React

  • build.gradle
    • Add frontend plugin, add DCE plugin, add node.js, add webpack configs
buildscript {
    repositories {
        maven { url "https://dl.bintray.com/kotlin/kotlin-eap" }
        maven { url "https://plugins.gradle.org/m2/" }
    }
    dependencies {
        def kotlin_frontend_plugin_version = "0.0.27"
        def gradle_node_plugin_version = "1.2.0"
        classpath "org.jetbrains.kotlin:kotlin-frontend-plugin:" + kotlin_frontend_plugin_version
        classpath "com.moowork.gradle:gradle-node-plugin:" + gradle_node_plugin_version
    }
}

apply plugin: 'kotlinx-serialization'
apply plugin: 'org.jetbrains.kotlin.frontend'
apply plugin: 'kotlin2js'
apply plugin: 'kotlin-platform-js'
apply plugin: 'kotlin-dce-js'
apply plugin: 'com.moowork.node'

node {
    download = true
}
compileKotlin2Js.kotlinOptions.moduleKind = "commonjs"

kotlinFrontend {
    downloadNodeJsVersion = 'latest'
    npm {
        dependency("react", "16.2.0")
        dependency("react-dom", "16.2.0")
        devDependency("webpack-cli", "2.0.10")
    }
    webpackBundle {
        bundleName = "bundle"
        sourceMapEnabled = true
        publicPath = "/"
        port = 3000
        stats = "verbose"
    }
}

task buildBundle(type: NpmTask, dependsOn: [npmInstall, runDceKotlinJs]) {
    args = ["run", "bundle"]
}

task copyStatic {
    copy {
        from "src/main/static"
        into 'build'
    }
}

assemble.dependsOn(buildBundle, copyStatic)
run.dependsOn(copyStatic)

repositories {
    maven { url "https://kotlin.bintray.com/kotlin-js-wrappers" }
}

dependencies {
    compile project(":multiplat-workshop-js")
    expectedBy project(":multiplat-workshop-client")

    compile "org.jetbrains:kotlin-extensions:$js_wrappers_version"
    compile "org.jetbrains:kotlin-react:$react_wrapper_version"
    compile "org.jetbrains:kotlin-react-dom:$react_wrapper_version"

    compile "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$coroutines_version"
    compile "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version"
    compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version"
}

kotlin {
    experimental {
        coroutines "enable"
    }
}
  • gradle.properties
    • Add JS wrappers and React wrappers
js_wrappers_version=1.0.1-pre.11-kotlin-1.2.21
react_wrapper_version=16.2.1-pre.11-kotlin-1.2.21
  • static folder
    • Move index.html to static folder
    • Modify contents to display only bundle
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Kotlin/JS &lsquo;Hello, World&rsquo; Multiplatters</title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="bundle.js"></script>
</body>
</html>
  • PhotoComponent

interface PhotoProps : RProps {
    var photos: List<Photo>
}

class PhotoComponent : RComponent<PhotoProps, RState>() {
    override fun RBuilder.render() {
        props.photos.forEach {
            div {
                img {
                    attrs.alt = it.member.name
                    attrs.src = it.photo_link
                    attrs.height = "400"
                    attrs.width = "400"
                }
            }
        }
    }
}

fun RBuilder.reactPhotoComponent(photos: List<Photo> = listOf()) = child(PhotoComponent::class) {
    attrs.photos = photos
}
  • PhotoRenderer.kt
    • Modify to use reactDOM render function with our 'root' id as element to render our PhotoComponent
    actual fun render(photos: List<Photo>) {
        react.dom.render(document.getElementById("root")){
            reactPhotoComponent(photos)
        }
    }
  • webpack.config.js
    • Add webpack config to handle bundling of correct dependencies and serving via dev-server
var webpack = require("webpack");
var path = require("path");

module.exports = {
  entry: path.resolve(__dirname, "build/kotlin-js-min/main/multiplat-workshop-client-react.js"),
  output: {
    path: path.resolve(__dirname, "build"),
    filename: "bundle.js"
  },
  resolve: {
    modules : [
      "node_modules",
      path.resolve(__dirname, "build/kotlin-js-min/main/")
    ]
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin()
  ]
};
  • package.json
    • Add package.json to enable node.js build
{
  "dependencies": {
    "webpack": "3.4.1"
  },
  "scripts": {
    "bundle": "webpack"
  }
}

Client Desktop

  • gradle.properties
    • Add tornadofx version - tornadofx_version=1.7.15
  • build.gradle
    • Modify to contain tornado FX plugin and a main class
buildscript {
    dependencies {
        def javafx_gradle_plugin_version='8.8.2'
        classpath "de.dynamicfiles.projects.gradle.plugins:javafx-gradle-plugin:$javafx_gradle_plugin_version"
    }
}

apply plugin: 'kotlin-platform-jvm'
apply plugin: 'javafx-gradle-plugin'

dependencies {
    compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
    compile "no.tornado:tornadofx:$tornadofx_version"
    compile project(":multiplat-workshop-jvm")
    expectedBy project(":multiplat-workshop-client")
    compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
}

compileKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

jar {
    manifest {
        attributes 'Main-Class': 'MainKt'
    }
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}

jfx {
    mainClass = 'MainKt'
    vendor = 'jussi'
}

kotlin {
    experimental {
        coroutines = "enable"
    }
}
  • view/PhotoComponent
    • Add tornadoFX component
class PhotoComponent : View() {
    override val root = ScrollPane()

    init {
        with(root) {
            prefWidth = 800.0
            prefHeight = 600.0
            vbox {
                photos.forEach {
                    imageview(it.photo_link) {
                        scaleX = .50
                        scaleY = .50
                    }
                }
            }
        }
    }

    companion object {
        var photos = listOf<Photo>()
    }
}

class TornadoApplication : App() {
    override val primaryView = PhotoComponent::class
}
  • PhotoRenderer.kt
    • Add photos list to static field on Component
    • call launch with TornadoApplication type param
⚠️ **GitHub.com Fallback** ⚠️