diff --git a/build.sbt b/build.sbt
index 4bec638..0a4c549 100644
--- a/build.sbt
+++ b/build.sbt
@@ -42,7 +42,7 @@ libraryDependencies ++= Seq(
"org.scalafx" %% "scalafx" % "12.0.2-R18",
"org.scalafx" %% "scalafx-extras" % "0.3.4",
"com.softwaremill.sttp.client" %% "json4s" % "2.1.1",
- "org.json4s" %% "json4s-native" % "3.6.7",
+ "org.json4s" %% "json4s-jackson" % "3.6.8",
"org.scala-lang.modules" %% "scala-async" % "0.10.0",
"org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided,
// "org.kordamp.ikonli" %% "ikonli-javafx" % "11.4.0",
@@ -63,6 +63,9 @@ libraryDependencies += "com.jfoenix" % "jfoenix" % "9.0.9"
// https://mvnrepository.com/artifact/org.kordamp.bootstrapfx/bootstrapfx-core
libraryDependencies += "org.kordamp.bootstrapfx" % "bootstrapfx-core" % "0.2.4"
+// https://mvnrepository.com/artifact/org.json4s/json4s-ext
+libraryDependencies += "org.json4s" %% "json4s-ext" % "3.6.8"
+
enablePlugins(BuildInfoPlugin)
buildInfoPackage := "wow.doge.chatto"
diff --git a/src/main/resources/fxml/Chat.fxml b/src/main/resources/fxml/Chat.fxml
index b6f981c..780ad91 100644
--- a/src/main/resources/fxml/Chat.fxml
+++ b/src/main/resources/fxml/Chat.fxml
@@ -1,7 +1,6 @@
-
@@ -25,15 +24,24 @@
+
+
+
+
+
+
+
+
+
+
-
-
+
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index afaebf8..b8a33a0 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -8,6 +8,10 @@
+
+
+
+
diff --git a/src/main/scala/wow/doge/chatto/ApplicationController.scala b/src/main/scala/wow/doge/chatto/ApplicationController.scala
index a4a74b3..a3d6fc0 100644
--- a/src/main/scala/wow/doge/chatto/ApplicationController.scala
+++ b/src/main/scala/wow/doge/chatto/ApplicationController.scala
@@ -13,7 +13,9 @@ import scala.concurrent.ExecutionContext.Implicits.global
import scala.async.Async.{async, await}
import sttp.client.json4s._
import org.json4s._
-import org.json4s.native.JsonMethods._
+// import org.json4s.native.JsonMethods._
+import org.json4s.jackson.JsonMethods._
+
import org.json4s.JsonDSL._
import scala.util.Success
import scala.util.Failure
@@ -28,7 +30,7 @@ class ApplicationController extends DefaultWindowController {
// import ApplicationController._
lazy val mainViewController = getController[MainViewController]()
- private implicit lazy val serialization = org.json4s.native.Serialization
+ private implicit lazy val serialization = org.json4s.jackson.Serialization
private implicit lazy val backend = AsyncHttpClientFutureBackend()
// override def width: Int = 400
diff --git a/src/main/scala/wow/doge/chatto/control/UserBox2.scala b/src/main/scala/wow/doge/chatto/control/UserBox2.scala
new file mode 100644
index 0000000..df0aae0
--- /dev/null
+++ b/src/main/scala/wow/doge/chatto/control/UserBox2.scala
@@ -0,0 +1,12 @@
+package wow.doge.chatto.control
+
+import javafx.scene.layout.HBox
+import javafx.scene.control.Label
+import scalafx.Includes._
+
+class UserBox2(_username: String) extends HBox() {
+ val usernameLabel = new Label(_username)
+ this.children ++= Seq {
+ usernameLabel
+ }
+}
diff --git a/src/main/scala/wow/doge/chatto/controller/ChatController.scala b/src/main/scala/wow/doge/chatto/controller/ChatController.scala
index 7ae559c..2abbf38 100644
--- a/src/main/scala/wow/doge/chatto/controller/ChatController.scala
+++ b/src/main/scala/wow/doge/chatto/controller/ChatController.scala
@@ -24,57 +24,102 @@ import wow.doge.chatto.AppDataHandler
import com.jfoenix.controls.JFXListView
import scala.async.Async.{async, await}
import javafx.scene.paint.Color
+import scalafx.collections.ObservableBuffer
+import javafx.beans.property.SimpleListProperty
+import wow.doge.chatto.control.UserBox2
+import javafx.beans.value.ChangeListener
+import com.sfxcode.sapphire.core.value.KeyBindings
+import com.sfxcode.sapphire.core.value.FXBeanAdapter
+import scalafx.collections.ObservableMap
+import com.sfxcode.sapphire.core.value.BeanConversions
+import javafx.util.converter.DateStringConverter
class ChatController @Inject() (
userService: UserService,
appDataHandler: AppDataHandler
) extends AbstractViewController
- with LazyLogging {
+ with LazyLogging
+ with BeanConversions {
- @FXML private var label: Label = _
+ // @FXML private var label: Label = _
@FXML private var flowPane: FlowPane = _
@FXML private var submitButton: Button = _
@FXML private var logoutButton: Button = _
// @FXML private var chatTextArea: TextArea = _
@FXML private var chatInput: TextArea = _
@FXML private var usersVBox: VBox = _
- @FXML var usersListView: JFXListView[Any] = _
+ @FXML var usersListView: JFXListView[UserBox2] = _
@FXML private var chatListView: JFXListView[HBox] = _
+ @FXML private var curUsr: Label = _
+ @FXML private var dataContent: Label = _
// applicationController.show
+ private val usersBuffer = ObservableBuffer[FXBean[User]]()
+ private val chatDataBuffer = ObservableMap[String, FXBean[User]]()
+
+ private lazy val curUserKeys = KeyBindings("curUser")
+ // bindings.add("person", "Person ${_self.name()} with age of ${_self.age()} is active: ${_self.isActive()}")
+ private lazy val curUserAdapter = FXBeanAdapter[User](this)
override def didGainVisibilityFirstTime(): Unit = {
super.didGainVisibilityFirstTime()
- val ub = new UserBox()
+ // val ub = new UserBox()
this.stage.resizable = true
- ub.messageLabel.text = "Hi there"
+ // ub.messageLabel.text = "Hi there"
// ub.messageLabel.id =
- ub.userRadioButton.text = "User 1"
+ // ub.userRadioButton.text = "User 1"
// usersVBox.children.clear()
// usersVBox.children += ub
- println("test")
- println(s"Result = ${func()}")
- offFX(println("hello from new thread"))
+ // println("test")
+ // println(s"Result = ${func()}")
+ // offFX(println("hello from new thread"))
// chatTextArea.visible <== !chatInput.text.isEmpty
// chatTextArea.text <== chatInput.text
- for (r <- userService.func2()) yield (logger.info(s"${r.body}"))
+ // for (r <- userService.func2()) yield (logger.info(s"${r.body}"))
- val person = Person(0, 10, "Billy")
- val bean = FXBean[Person](person)
- ub.messageLabel.text <== bean.getStringProperty("name")
+ // val person = Person(0, 10, "Billy")
+ // val bean = FXBean[Person](person)
+ // ub.messageLabel.text <== bean.getStringProperty("name")
// bean.getStringProperty("name") <== chatInput.text
// bean.getStringProperty("name")() = "Lester"
- println(bean.getValue("name"))
- println(bean.getStringProperty("name")())
+ // println(bean.getValue("name"))
+ // println(bean.getStringProperty("name")())
// bean.
- bean.updateValue("name", "Lester")
- println(bean.bean)
+ // bean.updateValue("name", "Lester")
+ // println(bean.bean)
- logoutButton.onAction = (e) => {
- applicationController.logout()
- println(appDataHandler.appData)
- }
+ // val curUserBean = FXBean[User](User("None"))
+
+ // logoutButton.onAction = (e) =>
+
+ // usersListView
+ // .getSelectionModel()
+ // .selectedItemProperty()
+ // .addListener((_, _, newValue) => {
+ // curUsr.text() = newValue.usernameLabel.text()
+ // })
+ curUserKeys.add("content", "${_self.data().content()}")
+ // curUserAdapter.addDateConverter()
+ curUserAdapter.addBindings(curUserKeys)
+
+ usersListView
+ .getSelectionModel()
+ .selectedItemProperty()
+ .addListener((_, _, newValue) => {
+ if (newValue != null) {
+ val dataBean =
+ FXBean(User(newValue.usernameLabel.text(), Data("test data")))
+ curUserAdapter
+ .set(dataBean)
+ }
+ })
+
+ // curUsr.text <== usersListView
+ // .getSelectionModel()
+ // .getSelectedItem()
+ // .username
+ // .text
}
@@ -86,9 +131,19 @@ class ChatController @Inject() (
val willBeUsers = userService
.getUsers(appDataHandler.appData.credentials)
.map(_.body)
+ val willBeActiveUsers = userService
+ .getActiveUsers(appDataHandler.appData.credentials)
+ .map(_.body)
val maybeUsers = await(willBeUsers)
+ val maybeActiveUsers = await(willBeActiveUsers)
+
+ // maybeActiveUsers.foreach(println)
+ logger.debug(s"$maybeActiveUsers")
+
val maybeUsersBoxes = maybeUsers.map(users => {
+ // usersBuffer ++= users
users.map(user => {
+ // usersBuffer += FXBean(User(user))
// val ub = new UserBox()
// ub.messageLabel.text = "Hi there"
// ub.userRadioButton.text = user
@@ -96,12 +151,12 @@ class ChatController @Inject() (
// new Rectangle {
// fill <== when(hover) choose (Color.Red)
// }
- val hb = new HBox
- new HBox {
- this.children += new Label {
- textProperty() = user
- // textFillProperty <== when(this.hover) choose (Color.RED) otherwise (Color.BLUE)
- }
+ // val hb = new HBox
+ new UserBox2(user) {
+ // this.children += new Label {
+ // textProperty() = user
+ // // textFillProperty <== when(this.hover) choose (Color.RED) otherwise (Color.BLUE)
+ // }
// fill <== when(this.hover) choose (Color.RED) otherwise (Color.BLUE)
}
})
@@ -109,6 +164,8 @@ class ChatController @Inject() (
onFX {
maybeUsersBoxes.map(userBoxes => {
usersListView.items() ++= userBoxes
+ // val x = new SimpleListProperty(ObservableBuffer(userBoxes))
+ // usersListView.items <== x
})
}
}
@@ -119,6 +176,24 @@ class ChatController @Inject() (
}
x
}
+
+ def actionLogout = {
+ offFXAndWait {
+ appDataHandler.clearCredentials()
+ println(appDataHandler.appData)
+ }
+ curUserAdapter.set(User.empty)
+ usersListView.items().clear()
+ chatListView.items().clear()
+ applicationController.logout()
+ println(appDataHandler.appData)
+ }
}
final case class Person(id: Int, age: Int, name: String)
+final case class Data(content: String)
+final case class User(curUser: String, data: Data)
+object User {
+ def empty = User("", Data(""))
+}
+// final case class chatData()
diff --git a/src/main/scala/wow/doge/chatto/controller/LoginController.scala b/src/main/scala/wow/doge/chatto/controller/LoginController.scala
index 7a0ef69..3fda1ed 100644
--- a/src/main/scala/wow/doge/chatto/controller/LoginController.scala
+++ b/src/main/scala/wow/doge/chatto/controller/LoginController.scala
@@ -65,7 +65,7 @@ class LoginController @Inject() (
val box = new VBox()
val adapter = FXBeanAdapter[Person](this)
- val bean = FXBean[Person](Person("twar", "username", "password"))
+ val bean = FXBean[Person](Person("0", "username", "password"))
adapter.addBindings(bindings)
adapter.set(bean)
// adapter.addIntConverter("age")
@@ -96,9 +96,9 @@ class LoginController @Inject() (
val credentials =
UserCredentials(inputUserName, inputPassword, token)
appDataHandler.updateCredentials(credentials)
- await {
- userService.getUsers(credentials).map(_.body.foreach(println))
- }
+ // await {
+ // userService.getUsers(credentials).map(_.body.foreach(println))
+ // }
applicationController.showChatPane()
}
case None => {
diff --git a/src/main/scala/wow/doge/chatto/service/CustomSerializer.scala b/src/main/scala/wow/doge/chatto/service/CustomSerializer.scala
new file mode 100644
index 0000000..dd7fcba
--- /dev/null
+++ b/src/main/scala/wow/doge/chatto/service/CustomSerializer.scala
@@ -0,0 +1,69 @@
+package wow.doge.chatto.service
+
+import org.json4s.CustomSerializer
+import org.json4s._
+import java.time.Instant
+// import org.json4s.JsonAST._
+import org.json4s.JsonDSL._
+// class ParentSerializer extends Serializer[Parent] {
+// private val ParentClass = classOf[Parent]
+// implicit val formats = DefaultFormats
+
+// def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), Parent] = {
+// case (TypeInfo(ParentClass, _), json) => json match {
+// case JObject(JField("kind", JString(kind)) :: _) => kind match {
+// case "first_type" => json.extract[ChildClassOne]
+// case "second_type" => json.extract[ChildClassTwo]
+// }
+
+// case _ => throw new MappingException("Invalid kind")
+// }
+// }
+
+// def serialize(implicit format: Formats): PartialFunction[Any, JValue] = Map()
+// }
+
+// class MyJInstantSerializer extends Serializer[Instant] {
+// implicit val formats = DefaultFormats
+
+// def deserialize(
+// implicit format: Formats
+// ): PartialFunction[(TypeInfo, JValue), Instant] = {
+// case (_, JString(d)) => Instant.parse(d)
+// case (_, JNull) => null
+// }
+
+// def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
+// case d: Instant => JString(d.toString())
+// }
+// }
+
+// class MyJInstantSerializer
+// extends CustomSerializer[Instant](implicit format =>
+// (
+// {
+// // case JInt(d) => Instant.ofEpochMilli(d.toLong)
+// case JString(d) => Instant.parse(d)
+// case JNull => null
+// }, {
+// // case d: Instant => JInt(d.toEpochMilli)
+// case d: Instant => JString(d.toString())
+// }
+// )
+// )
+// import java.time.ZonedDateTime
+// import java.time.format.DateTimeFormatter
+
+// a custom serializer has two partial functions, one
+// for serializing and one for deserializing
+// case object ZDTSerializer
+// extends CustomSerializer[ZonedDateTime](format =>
+// ({
+// case JString(s) => ZonedDateTime.parse(s)
+// }, {
+// case zdt: ZonedDateTime =>
+// JString(
+// zdt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"))
+// )
+// })
+// )
diff --git a/src/main/scala/wow/doge/chatto/service/UserService.scala b/src/main/scala/wow/doge/chatto/service/UserService.scala
index d506be4..8d450ce 100644
--- a/src/main/scala/wow/doge/chatto/service/UserService.scala
+++ b/src/main/scala/wow/doge/chatto/service/UserService.scala
@@ -9,7 +9,6 @@ import sttp.client._
import scala.concurrent.Future
import sttp.client.asynchttpclient.WebSocketHandler
import javax.inject.Inject
-import scala.async.Async.{async, await}
import scala.util.Success
import scala.util.Failure
import wow.doge.chatto.AppData
@@ -20,17 +19,23 @@ import wow.doge.chatto.ApplicationController
import wow.doge.chatto.UserCredentials
import javax.inject._
import wow.doge.chatto.AppDataHandler
+import org.json4s.jackson.JsonMethods._
+import org.json4s.ext.JavaTimeSerializers
+import java.time.ZonedDateTime
+import org.json4s.jackson.Serialization._
class UserService @Inject() (appDataHandler: AppDataHandler)(
implicit backend: HttpBackend
) extends LazyLogging {
- private implicit lazy val serialization = org.json4s.native.Serialization
+ private implicit lazy val serialization = org.json4s.jackson.Serialization
+ private implicit lazy val formats =
+ DefaultFormats ++ JavaTimeSerializers.all
private val domain = "http://localhost:8080"
private lazy val baseUrl = uri"$domain/api/chat"
private lazy val authBasicRequest = (credentials: UserCredentials) =>
basicRequest.auth
.basic(credentials.username, credentials.password)
- // .header("X-AUTH-TOKEN", appData.credentials.token)
+ .header("X-AUTH-TOKEN", credentials.token)
def func1() = async {
val willBeResponse = func2()
@@ -47,7 +52,12 @@ class UserService @Inject() (appDataHandler: AppDataHandler)(
private def endpoint(uri: String) = uri"$baseUrl/$uri"
def getUsers(credentials: UserCredentials) = async {
- println(appDataHandler.appData)
+ logger.debug(s"${appDataHandler.appData}")
+ println(
+ write[ActiveUser](
+ ActiveUser("hmm what is it", true, Some(ZonedDateTime.now()))
+ )
+ )
await {
authBasicRequest(credentials)
.get(uri"http://localhost:8080/api/chat/get/users")
@@ -56,6 +66,22 @@ class UserService @Inject() (appDataHandler: AppDataHandler)(
}
}
+ def getMessages(credentials: UserCredentials) = async {
+ logger.debug(s"${appDataHandler.appData}")
+ await {
+ authBasicRequest(credentials)
+ .get(uri"http://localhost:8080/api/chat/get/users")
+ .response(asJson[List[String]])
+ .send()
+ }
+ }
+
+ def getActiveUsers(credentials: UserCredentials) =
+ authBasicRequest(credentials)
+ .get(uri"http://localhost:8080/api/chat/get/active-users")
+ .response(asJson[List[ActiveUser]])
+ .send()
+
}
final case class HttpBinResponse(
@@ -63,3 +89,14 @@ final case class HttpBinResponse(
origin: String,
headers: Map[String, String]
)
+
+final case class ActiveUser(
+ userName: String,
+ online: Boolean,
+ lastActive: Option[ZonedDateTime]
+)
+
+object ActiveUser {
+ val z = ZonedDateTime.now()
+ z.toInstant().toString()
+}