Skip to content

Commit

Permalink
New method for computing User updates that uses ETag (#163)
Browse files Browse the repository at this point in the history
* New method for computing User updates that uses ETag

Fixes the problem of overwriting custom data at regular intervals
when `storeUserActivityInterval` is enabled.

* PubNub kotlin 0.11.0 release.

---------

Co-authored-by: PubNub Release Bot <[email protected]>
  • Loading branch information
wkal-pubnub and pubnub-release-bot authored Jan 23, 2025
1 parent 9703498 commit d59ff18
Show file tree
Hide file tree
Showing 13 changed files with 317 additions and 28 deletions.
19 changes: 16 additions & 3 deletions .pubnub.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: kmp-chat
version: 0.10.1
version: 0.11.0
schema: 1
scm: github.com/pubnub/kmp-chat
sdks:
Expand All @@ -21,8 +21,8 @@ sdks:
-
distribution-type: library
distribution-repository: maven
package-name: pubnub-chat-0.10.1
location: https://repo.maven.apache.org/maven2/com/pubnub/pubnub-chat/0.10.1/
package-name: pubnub-chat-0.11.0
location: https://repo.maven.apache.org/maven2/com/pubnub/pubnub-chat/0.11.0/
supported-platforms:
supported-operating-systems:
Android:
Expand Down Expand Up @@ -77,6 +77,19 @@ sdks:
license-url: https://github.com/pubnub/kotlin/blob/master/LICENSE
is-required: Required
changelog:
- date: 2025-01-23
version: 0.11.0
changes:
- type: feature
text: "Added a new version of `User.update` that can be used to update data on the server without losing intermediate updates that might have happened in the time between when the object was last received and updated."
- type: feature
text: "Added the ability to mute and unmute users through `Chat.mutedUsers.muteUser() / unmuteUser()`."
- type: feature
text: "Added the option to automatically sync the mute list using App Context by enabling `ChatConfiguration.syncMutedUsers`."
- type: feature
text: "Added missing function to parse quoted message text into parts."
- type: bug
text: "Fixes the problem of overwriting custom data at regular intervals when `storeUserActivityInterval` is enabled."
- date: 2025-01-13
version: 0.10.1
changes:
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ let package = Package(
targets: [
.binaryTarget(
name: "PubNubChatRemoteBinaryPackage",
url: "https://github.com/pubnub/kmp-chat/releases/download/kotlin-0.10.1/PubNubChat.xcframework.zip",
url: "https://github.com/pubnub/kmp-chat/releases/download/kotlin-0.11.0/PubNubChat.xcframework.zip",
checksum: "3153a4429665fe51861cfdc3b63cdb873260e777dcb41d52f14301698b2b2a91"
)
]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ You will need the publish and subscribe keys to authenticate your app. Get your
<dependency>
<groupId>com.pubnub</groupId>
<artifactId>pubnub-chat</artifactId>
<version>0.10.1</version>
<version>0.11.0</version>
</dependency>
```

Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ SONATYPE_HOST=DEFAULT
SONATYPE_AUTOMATIC_RELEASE=false
GROUP=com.pubnub
POM_PACKAGING=jar
VERSION_NAME=0.11.1
VERSION_NAME=0.11.0

POM_NAME=PubNub Chat SDK
POM_DESCRIPTION=This SDK offers a set of handy methods to create your own feature-rich chat or add a chat to your existing application.
Expand Down
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ ktlint = "12.1.0"
dokka = "1.9.20"
kotlinx_serialization = "1.7.3"
kotlinx_coroutines = "1.9.0"
pubnub = "10.3.4"
pubnub_swift = "8.2.4"
pubnub = "10.4.0"
pubnub_swift = "8.3.0"

[libraries]
pubnub-kotlin-api = { module = "com.pubnub:pubnub-kotlin-api", version.ref = "pubnub" }
Expand Down
16 changes: 16 additions & 0 deletions kotlin-js-store/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,22 @@ [email protected]:
react-native-url-polyfill "^2.0.0"
text-encoding "^0.7.0"

[email protected]:
version "8.6.0"
resolved "https://registry.yarnpkg.com/pubnub/-/pubnub-8.6.0.tgz#75524e7ed3653090652d160ce83ac089362a0379"
integrity sha512-LBCglooxiLkNT3ArUOvSJnLKK6/QdeshWY60IWlSQ+SkXlzEjt74wAnX5XriEXKsmza2yw9mFGG6+B5SlczRzA==
dependencies:
agentkeepalive "^3.5.2"
buffer "^6.0.3"
cbor-js "^0.1.0"
cbor-sync "^1.0.4"
form-data "^4.0.0"
lil-uuid "^0.1.1"
node-fetch "^2.7.0"
proxy-agent "^6.3.0"
react-native-url-polyfill "^2.0.0"
text-encoding "^0.7.0"

punycode@^2.1.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
Expand Down
22 changes: 22 additions & 0 deletions pubnub-chat-api/api/pubnub-chat-api.api
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ public abstract interface class com/pubnub/chat/User {
public static synthetic fun getChannelsRestrictions$default (Lcom/pubnub/chat/User;Ljava/lang/Integer;Lcom/pubnub/api/models/consumer/objects/PNPage;Ljava/util/Collection;ILjava/lang/Object;)Lcom/pubnub/kmp/PNFuture;
public abstract fun getChat ()Lcom/pubnub/chat/Chat;
public abstract fun getCustom ()Ljava/util/Map;
public abstract fun getETag ()Ljava/lang/String;
public abstract fun getEmail ()Ljava/lang/String;
public abstract fun getExternalId ()Ljava/lang/String;
public abstract fun getId ()Ljava/lang/String;
Expand All @@ -347,13 +348,34 @@ public abstract interface class com/pubnub/chat/User {
public static synthetic fun setRestrictions$default (Lcom/pubnub/chat/User;Lcom/pubnub/chat/Channel;ZZLjava/lang/String;ILjava/lang/Object;)Lcom/pubnub/kmp/PNFuture;
public abstract fun streamUpdates (Lkotlin/jvm/functions/Function1;)Ljava/lang/AutoCloseable;
public abstract fun update (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)Lcom/pubnub/kmp/PNFuture;
public abstract fun update (Lkotlin/jvm/functions/Function2;)Lcom/pubnub/kmp/PNFuture;
public static synthetic fun update$default (Lcom/pubnub/chat/User;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/pubnub/kmp/PNFuture;
public abstract fun wherePresent ()Lcom/pubnub/kmp/PNFuture;
}

public final class com/pubnub/chat/User$Companion {
}

public final class com/pubnub/chat/User$UpdatableValues {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getCustom ()Ljava/lang/Object;
public final fun getEmail ()Ljava/lang/String;
public final fun getExternalId ()Ljava/lang/String;
public final fun getName ()Ljava/lang/String;
public final fun getProfileUrl ()Ljava/lang/String;
public final fun getStatus ()Ljava/lang/String;
public final fun getType ()Ljava/lang/String;
public final fun setCustom (Ljava/lang/Object;)V
public final fun setEmail (Ljava/lang/String;)V
public final fun setExternalId (Ljava/lang/String;)V
public final fun setName (Ljava/lang/String;)V
public final fun setProfileUrl (Ljava/lang/String;)V
public final fun setStatus (Ljava/lang/String;)V
public final fun setType (Ljava/lang/String;)V
}

public abstract interface class com/pubnub/chat/config/ChatConfiguration {
public abstract fun getCustomPayloads ()Lcom/pubnub/chat/config/CustomPayloads;
public abstract fun getLogLevel ()Lcom/pubnub/chat/config/LogLevel;
Expand Down
65 changes: 64 additions & 1 deletion pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/User.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,17 @@ interface User {
val type: String?

/**
* Type of the user, like admin, member, guest.
* The moment in time when the data contained in this User object was updated on the server.
*/
val updated: String?

/**
* The eTag that was returned by the server with this User object.
*
* It is a random string that changes with each data update.
*/
val eTag: String?

/**
* Timestamp for the last time the user information was updated or modified.
*/
Expand Down Expand Up @@ -98,6 +105,28 @@ interface User {
type: String? = null,
): PNFuture<User>

/**
* Updates the metadata of the user with information provided in [updateAction].
*
* Please note that `updateAction` will be called _at least_ once with the current data from the `User` object in
* the argument. Inside `updateAction`, new values for `User` fields should be computed and assigned into the
* context `UpdatedValues` object.
*
* In case the user's information has changed on the server since the original User object was retrieved, the
* `updateAction` will be called again with new User data that represents the current server state. This might
* happen multiple times until either new data is saved successfully, or the request fails.
*
* @param updateAction a function for computing new values for the User fields based on the provided `user` argument
* and saving it into the `UpdatedValues` context object.
*
* @return [PNFuture] containing the updated [User].
*/
fun update(
updateAction: UpdatableValues.(
user: User
) -> Unit
): PNFuture<User>

/**
* Deletes the user. If soft deletion is enabled, the user's data is retained but marked as inactive.
*
Expand Down Expand Up @@ -202,5 +231,39 @@ interface User {
*/
operator fun plus(update: PNUUIDMetadata): User

/**
* An object representing the fields to update in a [User] object.
*/
class UpdatableValues(
/**
* The new value for [User.name].
*/
var name: String? = null,
/**
* The new value for [User.externalId].
*/
var externalId: String? = null,
/**
* The new value for [User.profileUrl].
*/
var profileUrl: String? = null,
/**
* The new value for [User.email].
*/
var email: String? = null,
/**
* The new value for [User.custom].
*/
var custom: CustomObject? = null,
/**
* The new value for [User.status].
*/
var status: String? = null,
/**
* The new value for [User.type].
*/
var type: String? = null,
)

companion object
}
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,16 @@ class ChatImpl(
if (user != null) {
log.pnError(USER_ID_ALREADY_EXIST)
} else {
setUserMetadata(id, name, externalId, profileUrl, email, custom, type, status)
setUserMetadata(
id = id,
name = name,
externalId = externalId,
profileUrl = profileUrl,
email = email,
custom = custom,
type = type,
status = status
)
}
}
}
Expand Down Expand Up @@ -1074,10 +1083,6 @@ class ChatImpl(
return config.pushNotifications
}

private fun isValidId(id: String): Boolean {
return id.isNotEmpty()
}

private fun performSoftUserDelete(user: User): PNFuture<User> {
val updatedUser = (user as UserImpl).copy(status = DELETED)
return pubNub.setUUIDMetadata(
Expand Down Expand Up @@ -1260,16 +1265,15 @@ class ChatImpl(
}

private fun saveTimeStampFunc(): PNFuture<Unit> {
val customWithUpdatedLastActiveTimestamp = buildMap {
currentUser.custom?.let { putAll(it) }
put(LAST_ACTIVE_TIMESTAMP, Clock.System.now().toEpochMilliseconds().toString())
}
return pubNub.setUUIDMetadata(
uuid = currentUser.id,
custom = createCustomObject(customWithUpdatedLastActiveTimestamp),
includeCustom = true,
).then { pnUUIDMetadataResult: PNUUIDMetadataResult ->
currentUser = UserImpl.fromDTO(this, pnUUIDMetadataResult.data)
return currentUser.update { user ->
this.custom = createCustomObject(
buildMap {
user.custom?.let { putAll(it) }
put(LAST_ACTIVE_TIMESTAMP, Clock.System.now().toEpochMilliseconds().toString())
}
)
}.then { updatedUser ->
currentUser = updatedUser
}
}

Expand All @@ -1294,3 +1298,7 @@ class ChatImpl(
}

internal expect fun generateRandomUuid(): String

internal fun isValidId(id: String): Boolean {
return id.isNotEmpty()
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ internal const val DELETED = "deleted"
internal const val ORIGINAL_PUBLISHER = "originalPublisher"
internal const val ORIGINAL_CHANNEL_ID = "originalChannelId"
internal const val HTTP_ERROR_404 = 404
internal const val HTTP_ERROR_412 = 412
internal const val INTERNAL_MODERATION_PREFIX = "PUBNUB_INTERNAL_MODERATION_"
internal const val INTERNAL_USER_MODERATION_CHANNEL_PREFIX = "PUBNUB_INTERNAL_MODERATION."
internal const val PUBNUB_INTERNAL_AUTOMODERATED = "PUBNUB_INTERNAL_AUTOMODERATED"
Expand Down
Loading

0 comments on commit d59ff18

Please sign in to comment.