diff --git a/ChatExample/app/build.gradle b/ChatExample/app/build.gradle
index 83d08b6..6ddf2db 100644
--- a/ChatExample/app/build.gradle
+++ b/ChatExample/app/build.gradle
@@ -1,15 +1,12 @@
apply plugin: 'com.android.application'
-
apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-android-extensions'
-
android {
- compileSdkVersion 28
+ compileSdkVersion 33
defaultConfig {
applicationId "com.github.dsrees.chatexample"
minSdkVersion 19
- targetSdkVersion 28
+ targetSdkVersion 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -21,6 +18,10 @@ android {
}
}
+ buildFeatures {
+ viewBinding true
+ }
+
compileOptions {
targetCompatibility = "8"
sourceCompatibility = "8"
@@ -29,25 +30,21 @@ android {
dependencies {
/*
- To update the JavaPhoenixClient, either use the latest dependency from jcenter
+ To update the JavaPhoenixClient, either use the latest dependency from mavenCentral()
OR run
`./gradlew jar`
and copy
`/build/lib/*.jar` to `/ChatExample/app/libs`
- and comment out the jcenter dependency
+ and comment out the mavenCentral() dependency
*/
implementation fileTree(dir: 'libs', include: ['*.jar'])
-// implementation 'com.github.dsrees:JavaPhoenixClient:0.2.3'
-
-
- implementation "com.google.code.gson:gson:2.8.5"
- implementation "com.squareup.okhttp3:okhttp:3.12.2"
-
+// implementation 'com.github.dsrees:JavaPhoenixClient:0.3.4'
- implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation 'androidx.appcompat:appcompat:1.0.2'
- implementation 'androidx.recyclerview:recyclerview:1.0.0'
- implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ implementation "com.google.code.gson:gson:2.10.1"
+ implementation "com.squareup.okhttp3:okhttp:4.11.0"
+ implementation 'androidx.appcompat:appcompat:1.6.1'
+ implementation 'androidx.recyclerview:recyclerview:1.3.1'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
}
diff --git a/ChatExample/app/libs/JavaPhoenixClient-0.3.0.jar b/ChatExample/app/libs/JavaPhoenixClient-0.3.0.jar
deleted file mode 100644
index 4aa7d32..0000000
Binary files a/ChatExample/app/libs/JavaPhoenixClient-0.3.0.jar and /dev/null differ
diff --git a/ChatExample/app/libs/JavaPhoenixClient-1.1.5.jar b/ChatExample/app/libs/JavaPhoenixClient-1.1.5.jar
new file mode 100644
index 0000000..dae2106
Binary files /dev/null and b/ChatExample/app/libs/JavaPhoenixClient-1.1.5.jar differ
diff --git a/ChatExample/app/src/main/AndroidManifest.xml b/ChatExample/app/src/main/AndroidManifest.xml
index 5dbb22a..5ed4835 100644
--- a/ChatExample/app/src/main/AndroidManifest.xml
+++ b/ChatExample/app/src/main/AndroidManifest.xml
@@ -1,24 +1,28 @@
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.github.dsrees.chatexample">
-
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/ChatExample/app/src/main/java/com/github/dsrees/chatexample/MainActivity.kt b/ChatExample/app/src/main/java/com/github/dsrees/chatexample/MainActivity.kt
index 3acdcb5..9a4caa5 100644
--- a/ChatExample/app/src/main/java/com/github/dsrees/chatexample/MainActivity.kt
+++ b/ChatExample/app/src/main/java/com/github/dsrees/chatexample/MainActivity.kt
@@ -3,11 +3,8 @@ package com.github.dsrees.chatexample
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
-import android.widget.ArrayAdapter
-import android.widget.Button
-import android.widget.EditText
import androidx.recyclerview.widget.LinearLayoutManager
-import kotlinx.android.synthetic.main.activity_main.*
+import com.github.dsrees.chatexample.databinding.ActivityMainBinding
import org.phoenixframework.Channel
import org.phoenixframework.Socket
@@ -17,44 +14,47 @@ class MainActivity : AppCompatActivity() {
const val TAG = "MainActivity"
}
+ private lateinit var binding: ActivityMainBinding
+
private val messagesAdapter = MessagesAdapter()
private val layoutManager = LinearLayoutManager(this)
// Use when connecting to https://github.com/dwyl/phoenix-chat-example
- private val socket = Socket("https://phxchat.herokuapp.com/socket/websocket")
+ private val socket = Socket("https://phoenix-chat.fly.dev/socket/websocket")
private val topic = "room:lobby"
// Use when connecting to local server
-// private val socket = Socket("ws://10.0.2.2:4000/socket/websocket")
-// private val topic = "rooms:lobby"
+ // private val socket = Socket("ws://10.0.2.2:4000/socket/websocket")
+ // private val topic = "rooms:lobby"
private var lobbyChannel: Channel? = null
private val username: String
- get() = username_input.text.toString()
+ get() = binding.usernameInput.text.toString()
private val message: String
- get() = message_input.text.toString()
+ get() = binding.messageInput.text.toString()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
+ this.binding = ActivityMainBinding.inflate(layoutInflater)
+ setContentView(binding.root)
layoutManager.stackFromEnd = true
- messages_recycler_view.layoutManager = layoutManager
- messages_recycler_view.adapter = messagesAdapter
+ binding.messagesRecyclerView.layoutManager = layoutManager
+ binding.messagesRecyclerView.adapter = messagesAdapter
socket.onOpen {
this.addText("Socket Opened")
- runOnUiThread { connect_button.text = "Disconnect" }
+ runOnUiThread { binding.connectButton.text = "Disconnect" }
}
socket.onClose {
this.addText("Socket Closed")
- runOnUiThread { connect_button.text = "Connect" }
+ runOnUiThread { binding.connectButton.text = "Connect" }
}
socket.onError { throwable, response ->
@@ -67,7 +67,7 @@ class MainActivity : AppCompatActivity() {
}
- connect_button.setOnClickListener {
+ binding.connectButton.setOnClickListener {
if (socket.isConnected) {
this.disconnectAndLeave()
} else {
@@ -76,7 +76,7 @@ class MainActivity : AppCompatActivity() {
}
}
- send_button.setOnClickListener { sendMessage() }
+ binding.sendButton.setOnClickListener { sendMessage() }
}
private fun sendMessage() {
@@ -85,7 +85,7 @@ class MainActivity : AppCompatActivity() {
?.receive("ok") { Log.d(TAG, "success $it") }
?.receive("error") { Log.d(TAG, "error $it") }
- message_input.text.clear()
+ binding.messageInput.text.clear()
}
private fun disconnectAndLeave() {
@@ -132,9 +132,7 @@ class MainActivity : AppCompatActivity() {
private fun addText(message: String) {
runOnUiThread {
this.messagesAdapter.add(message)
- layoutManager.smoothScrollToPosition(messages_recycler_view, null, messagesAdapter.itemCount)
+ layoutManager.smoothScrollToPosition(binding.messagesRecyclerView, null, messagesAdapter.itemCount)
}
-
}
-
}
diff --git a/ChatExample/app/src/main/java/com/github/dsrees/chatexample/MessagesAdapter.kt b/ChatExample/app/src/main/java/com/github/dsrees/chatexample/MessagesAdapter.kt
index e99b294..143df58 100644
--- a/ChatExample/app/src/main/java/com/github/dsrees/chatexample/MessagesAdapter.kt
+++ b/ChatExample/app/src/main/java/com/github/dsrees/chatexample/MessagesAdapter.kt
@@ -5,6 +5,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
+import com.github.dsrees.chatexample.databinding.ItemMessageBinding
class MessagesAdapter : RecyclerView.Adapter() {
@@ -17,8 +18,8 @@ class MessagesAdapter : RecyclerView.Adapter() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
- val view = LayoutInflater.from(parent.context).inflate(R.layout.item_message, parent, false)
- return ViewHolder(view)
+ val binding = ItemMessageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ return ViewHolder(binding)
}
override fun getItemCount(): Int = messages.size
@@ -27,8 +28,8 @@ class MessagesAdapter : RecyclerView.Adapter() {
holder.label.text = messages[position]
}
- inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
- val label: TextView = itemView.findViewById(R.id.item_message_label)
+ inner class ViewHolder(binding: ItemMessageBinding) : RecyclerView.ViewHolder(binding.root) {
+ val label: TextView = binding.itemMessageLabel
}
}
\ No newline at end of file
diff --git a/ChatExample/build.gradle b/ChatExample/build.gradle
index b39ac10..08b070b 100644
--- a/ChatExample/build.gradle
+++ b/ChatExample/build.gradle
@@ -1,24 +1,22 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.3.31'
+ ext.kotlin_version = '1.8.0'
repositories {
google()
- jcenter()
+ mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.4.1'
+ classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
- jcenter()
+ mavenCentral()
}
}
diff --git a/ChatExample/gradle/wrapper/gradle-wrapper.properties b/ChatExample/gradle/wrapper/gradle-wrapper.properties
index 3c4d55d..5fa6080 100644
--- a/ChatExample/gradle/wrapper/gradle-wrapper.properties
+++ b/ChatExample/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Tue May 14 11:28:07 EDT 2019
+#Wed Oct 04 12:35:15 EDT 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/README.md b/README.md
index 9960308..ecdb961 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# JavaPhoenixClient
-[ ](https://bintray.com/drees/java-phoenix-client/JavaPhoenixClient/_latestVersion)
+[](https://search.maven.org/search?q=g:%22com.github.dsrees%22%20AND%20a:%22JavaPhoenixClient%22)
[](https://travis-ci.com/dsrees/JavaPhoenixClient)
[](https://codecov.io/gh/dsrees/JavaPhoenixClient)
@@ -39,40 +39,86 @@ fun connectToChatRoom() {
}
```
+
+If you need to provide dynamic parameters that can change between calls to `connect()`, then you can pass a closure to the constructor
+
+```kotlin
+
+// Create the Socket
+var authToken = "abc"
+val socket = Socket("http://localhost:4000/socket/websocket", { mapOf("token" to authToken) })
+
+// Connect with query parameters "?token=abc"
+socket.connect()
+
+
+// later in time, connect with query parameters "?token=xyz"
+authToken = "xyz"
+socket.connect() // or internal reconnect logic kicks in
+```
+
+
You can also inject your own OkHttp Client into the Socket to provide your own configuration
```kotlin
-// Create the Socket with a pre-configured OkHttp Client
+// Configure your own OkHttp Client
val client = OkHttpClient.Builder()
.connectTimeout(1000, TimeUnit.MILLISECONDS)
.build()
+// Create Socket with your custom instances
val params = hashMapOf("token" to "abc123")
val socket = Socket("http://localhost:4000/socket/websocket",
- params,
- client)
+ params,
+ client)
```
+By default, the client use GSON to encode and decode JSON. If you prefer to manage this yourself, you
+can provide custom encode/decode functions in the `Socket` constructor.
+
+```kotlin
+
+// Configure your own GSON instance
+val gson = Gson.Builder().create()
+val encoder: EncodeClosure = {
+ // Encode a Map into JSON using your custom GSON instance or another JSON library
+ // of your choice (Moshi, etc)
+}
+val decoder: DecodeClosure = {
+ // Decode a JSON String into a `Message` object using your custom JSON library
+}
+
+// Create Socket with your custom instances
+val params = hashMapOf("token" to "abc123")
+val socket = Socket("http://localhost:4000/socket/websocket",
+ params,
+ encoder,
+ decoder)
+```
+
+
+
+
### Installation
-JavaPhoenixClient is hosted on JCenter. You'll need to make sure you declare `jcenter()` as one of your repositories
+JavaPhoenixClient is hosted on MavenCentral. You'll need to make sure you declare `mavenCentral()` as one of your repositories
```
repositories {
- jcenter()
+ mavenCentral()
}
```
and then add the library. See [releases](https://github.com/dsrees/JavaPhoenixClient/releases) for the latest version
```$xslt
dependencies {
- implementation 'com.github.dsrees:JavaPhoenixClient:0.3.4'
+ implementation 'com.github.dsrees:JavaPhoenixClient:1.3.1'
}
```
### Feedback
-Please submit in issue if you have any problems!
+Please submit in issue if you have any problems or questions! PRs are also welcome.
This library is built to mirror the [phoenix.js](https://hexdocs.pm/phoenix/js/) and [SwiftPhoenixClient](https://github.com/davidstump/SwiftPhoenixClient) libraries.
diff --git a/RELEASING.md b/RELEASING.md
index 75a44b4..891816d 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -7,3 +7,6 @@ Release Process
4. Tag: `git tag -a X.Y.Z -m "Version X.Y.Z"`
5. Push: `git push && git push --tags`
6. Add the new release with notes (https://github.com/dsrees/JavaPhoenixClient/releases).
+ 7. Publish to Maven Central by running `./gradlew clean publish`. Can only be done by dsrees until CI setup
+ 8. Close the staging repo here: https://s01.oss.sonatype.org/#stagingRepositories
+ 9. Release the closed repo
diff --git a/build.gradle b/build.gradle
index c0e2779..78e6a8a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,19 +1,36 @@
-buildscript { repositories { jcenter() } }
+buildscript {
+ repositories {
+ jcenter()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.vanniktech:gradle-maven-publish-plugin:0.14.2'
+ classpath 'org.jetbrains.dokka:dokka-gradle-plugin:1.4.30'
+ }
+}
+
plugins {
id 'java'
id 'jacoco'
- id 'org.jetbrains.kotlin.jvm' version '1.3.31'
- id 'nebula.project' version '6.0.3'
- id "nebula.maven-publish" version '9.5.4'
- id 'nebula.nebula-bintray' version '5.0.0'
+ id 'org.jetbrains.kotlin.jvm' version '1.8.0'
+}
+
+ext {
+ RELEASE_REPOSITORY_URL = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
+ SNAPSHOT_REPOSITORY_URL = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
}
+apply plugin: "org.jetbrains.dokka"
+apply plugin: "com.vanniktech.maven.publish"
+
group 'com.github.dsrees'
-version '0.3.4'
+version '1.3.1'
sourceCompatibility = 1.8
repositories {
+ jcenter()
mavenCentral()
}
@@ -30,16 +47,16 @@ test {
}
dependencies {
- compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
- compile "com.google.code.gson:gson:2.8.5"
- compile "com.squareup.okhttp3:okhttp:3.12.2"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
+ implementation "com.google.code.gson:gson:2.10.1"
+ implementation "com.squareup.okhttp3:okhttp:4.11.0"
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.1'
- testCompile group: 'com.google.truth', name: 'truth', version: '0.44'
- testCompile group: 'org.mockito', name: 'mockito-core', version: '2.27.0'
- testCompile group: 'com.nhaarman.mockitokotlin2', name: 'mockito-kotlin', version: '2.1.0'
+ testImplementation group: 'com.google.truth', name: 'truth', version: '1.1.3'
+ testImplementation group: 'org.mockito', name: 'mockito-core', version: '4.0.0'
+ testImplementation group: 'org.mockito.kotlin', name: 'mockito-kotlin', version: '4.0.0'
}
jacocoTestReport {
@@ -49,8 +66,6 @@ jacocoTestReport {
}
}
-
-
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
@@ -58,23 +73,3 @@ compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
-bintray {
- user = System.getenv('bintrayUser')
- key = System.getenv('bintrayApiKey')
- dryRun = false
- publish = true
- pkg {
- repo = 'java-phoenix-client'
- name = 'JavaPhoenixClient'
- userOrg = user
- websiteUrl = 'https://github.com/dsrees/JavaPhoenixClient'
- issueTrackerUrl = 'https://github.com/dsrees/JavaPhoenixClient/issues'
- vcsUrl = 'https://github.com/dsrees/JavaPhoenixClient.git'
- licenses = ['MIT']
- version {
- name = project.version
- vcsTag = project.version
- }
- }
- publications = ['nebula']
-}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..65bd7af
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,20 @@
+GROUP=com.github.dsrees
+POM_ARTIFACT_ID=JavaPhoenixClient
+VERSION_NAME=0.3.4
+
+POM_NAME=JavaPhoenixClient
+POM_DESCRIPTION=A phoenix channels client built for the JVM
+POM_INCEPTION_YEAR=2018
+
+POM_URL=https://github.com/dsrees/JavaPhoenixClient
+POM_SCM_URL=https://github.com/dsrees/JavaPhoenixClient.git
+POM_SCM_CONNECTION=scm:git:git://github.com/dsrees/JavaPhoenixClient.git
+POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/dsrees/JavaPhoenixClient.git
+
+POM_LICENCE_NAME=MIT License
+POM_LICENCE_URL=https://github.com/dsrees/JavaPhoenixClient/blob/master/LICENSE.md
+POM_LICENCE_DIST=repo
+
+POM_DEVELOPER_ID=dsrees
+POM_DEVELOPER_NAME=Daniel Rees
+POM_DEVELOPER_URL=https://github.com/dsrees/
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 05e0e01..aa98239 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu Jun 13 12:25:29 EDT 2019
+#Wed Oct 04 12:21:20 EDT 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
diff --git a/src/main/kotlin/org/phoenixframework/Channel.kt b/src/main/kotlin/org/phoenixframework/Channel.kt
index 4c97999..d6103ab 100644
--- a/src/main/kotlin/org/phoenixframework/Channel.kt
+++ b/src/main/kotlin/org/phoenixframework/Channel.kt
@@ -38,7 +38,7 @@ data class Binding(
*/
class Channel(
val topic: String,
- params: Payload,
+ paramsClosure: PayloadClosure,
internal val socket: Socket
) {
@@ -94,10 +94,10 @@ class Channel(
internal var timeout: Long
/** Params passed in through constructions and provided to the JoinPush */
- var params: Payload = params
+ var params: Payload
+ get() = joinPush.payload
set(value) {
joinPush.payload = value
- field = value
}
/** Set to true once the channel has attempted to join */
@@ -112,12 +112,21 @@ class Channel(
/** Timer to attempt rejoins */
internal var rejoinTimer: TimeoutTimer
+ /** Refs if stateChange hooks */
+ internal var stateChangeRefs: MutableList
+
/**
* Optional onMessage hook that can be provided. Receives all event messages for specialized
* handling before dispatching to the Channel event callbacks.
*/
internal var onMessage: (Message) -> Message = { it }
+ constructor(
+ topic: String,
+ params: Payload,
+ socket: Socket
+ ) : this(topic, { params }, socket)
+
init {
this.state = State.CLOSED
this.bindings = ConcurrentLinkedQueue()
@@ -125,6 +134,7 @@ class Channel(
this.timeout = socket.timeout
this.joinedOnce = false
this.pushBuffer = mutableListOf()
+ this.stateChangeRefs = mutableListOf()
this.rejoinTimer = TimeoutTimer(
dispatchQueue = socket.dispatchQueue,
timerCalculation = socket.rejoinAfterMs,
@@ -133,17 +143,18 @@ class Channel(
// Respond to socket events
this.socket.onError { _, _-> this.rejoinTimer.reset() }
+ .apply { stateChangeRefs.add(this) }
this.socket.onOpen {
this.rejoinTimer.reset()
if (this.isErrored) { this.rejoin() }
- }
+ }.apply { stateChangeRefs.add(this) }
// Setup Push to be sent when joining
this.joinPush = Push(
channel = this,
event = Event.JOIN.value,
- payload = params,
+ payloadClosure = paramsClosure,
timeout = timeout)
// Perform once the Channel has joined
@@ -203,7 +214,14 @@ class Channel(
this.socket.logItems("Channel: error $topic ${it.payload}")
// If error was received while joining, then reset the Push
- if (isJoining) { this.joinPush.reset() }
+ if (isJoining) {
+ // Make sure that the "phx_join" isn't buffered to send once the socket
+ // reconnects. The channel will send a new join event when the socket connects.
+ this.joinRef?.let { this.socket.removeFromSendBuffer(it) }
+
+ // Reset the push to be used again later
+ this.joinPush.reset()
+ }
// Mark the channel as errored and attempt to rejoin if socket is currently connected
this.state = State.ERRORED
@@ -212,7 +230,7 @@ class Channel(
// Perform when the join reply is received
this.on(Event.REPLY) { message ->
- this.trigger(replyEventName(message.ref), message.payload, message.ref, message.joinRef)
+ this.trigger(replyEventName(message.ref), message.rawPayload, message.ref, message.joinRef, message.payloadJson)
}
}
@@ -371,18 +389,20 @@ class Channel(
event: Event,
payload: Payload = hashMapOf(),
ref: String = "",
- joinRef: String? = null
+ joinRef: String? = null,
+ payloadJson: String = ""
) {
- this.trigger(event.value, payload, ref, joinRef)
+ this.trigger(event.value, payload, ref, joinRef, payloadJson)
}
internal fun trigger(
event: String,
payload: Payload = hashMapOf(),
ref: String = "",
- joinRef: String? = null
+ joinRef: String? = null,
+ payloadJson: String = ""
) {
- this.trigger(Message(ref, topic, event, payload, joinRef))
+ this.trigger(Message(joinRef, ref, topic, event, payload, payloadJson))
}
internal fun trigger(message: Message) {
@@ -414,6 +434,9 @@ class Channel(
// Do not attempt to rejoin if the channel is in the process of leaving
if (isLeaving) return
+ // Leave potentially duplicated channels
+ this.socket.leaveOpenTopic(this.topic)
+
// Send the joinPush
this.sendJoin(timeout)
}
diff --git a/src/main/kotlin/org/phoenixframework/Defaults.kt b/src/main/kotlin/org/phoenixframework/Defaults.kt
index b6c12be..06ecc9f 100644
--- a/src/main/kotlin/org/phoenixframework/Defaults.kt
+++ b/src/main/kotlin/org/phoenixframework/Defaults.kt
@@ -25,6 +25,10 @@ package org.phoenixframework
import com.google.gson.FieldNamingPolicy
import com.google.gson.Gson
import com.google.gson.GsonBuilder
+import com.google.gson.JsonObject
+import com.google.gson.reflect.TypeToken
+import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
+import java.net.URL
object Defaults {
@@ -34,9 +38,14 @@ object Defaults {
/** Default heartbeat interval of 30s */
const val HEARTBEAT: Long = 30_000
+ /** Default JSON Serializer Version set to 2.0.0 */
+ const val VSN: String = "2.0.0"
+
/** Default reconnect algorithm for the socket */
val reconnectSteppedBackOff: (Int) -> Long = { tries ->
- if (tries > 9) 5_000 else listOf(10L, 50L, 100L, 150L, 200L, 250L, 500L, 1_000L, 2_000L)[tries - 1]
+ if (tries > 9) 5_000 else listOf(
+ 10L, 50L, 100L, 150L, 200L, 250L, 500L, 1_000L, 2_000L
+ )[tries - 1]
}
/** Default rejoin algorithm for individual channels */
@@ -44,11 +53,110 @@ object Defaults {
if (tries > 3) 10_000 else listOf(1_000L, 2_000L, 5_000L)[tries - 1]
}
-
/** The default Gson configuration to use when parsing messages */
val gson: Gson
get() = GsonBuilder()
- .setLenient()
- .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
- .create()
+ .setLenient()
+ .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
+ .create()
+
+ /**
+ * Default JSON decoder, backed by GSON, that takes JSON and converts it
+ * into a Message object.
+ */
+ @Suppress("UNCHECKED_CAST")
+ val decode: DecodeClosure = { rawMessage ->
+
+ val parseValue: (String) -> String? = { value ->
+ when(value) {
+ "null" -> null
+ else -> value.replace("\"", "")
+ }
+ }
+
+ var message = rawMessage
+ message = message.removeRange(0, 1) // remove '['
+
+ val joinRef = message.takeWhile { it != ',' } // take "join ref", "null" or "\"5\""
+ message = message.removeRange(0, joinRef.length) // remove join ref
+ message = message.removeRange(0, 1) // remove ','
+
+ val ref = message.takeWhile { it != ',' } // take ref, "null" or "\"5\""
+ message = message.removeRange(0, ref.length) // remove ref
+ message = message.removeRange(0, 1) // remove ','
+
+ val topic = message.takeWhile { it != ',' } // take topic, "\"topic\""
+ message = message.removeRange(0, topic.length)
+ message = message.removeRange(0, 1) // remove ','
+
+ val event = message.takeWhile { it != ',' } // take event, "\"phx_reply\""
+ message = message.removeRange(0, event.length)
+ message = message.removeRange(0, 1) // remove ','
+
+ var remaining = message.removeRange(message.length - 1, message.length) // remove ']'
+
+ // Payload should now just be "{"message":"hello","from":"user_1"}" or
+ // "{"response": {"message":"hello","from":"user_1"}},"status":"ok"}", flatten.
+ val jsonObj = gson.fromJson(remaining, JsonObject::class.java)
+ val response = jsonObj.get("response")
+ val payload = response?.let { gson.toJson(response) } ?: remaining
+
+ val anyType = object : TypeToken