Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4.66.0 Release #3481

Merged
merged 11 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/vale-doc-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/[email protected]
- uses: errata-ai/vale-action@reviewdog
- uses: errata-ai/vale-action@v2.1.0
with:
# added, diff_context, file, nofilter
filter_mode: nofilter
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### πŸ”„ Changed

# [4.66.0](https://github.com/GetStream/stream-chat-swift/releases/tag/4.66.0)
_November 05, 2024_

## StreamChat
### βœ… Added
- Add support for system messages not updating `channel.lastMessageAt` [#3476](https://github.com/GetStream/stream-chat-swift/pull/3476)
- Add support for sending system messages client-side
[#3477](https://github.com/GetStream/stream-chat-swift/pull/3477)
### 🐞 Fixed
- Fix watching channels when performing channel search [#3472](https://github.com/GetStream/stream-chat-swift/pull/3472)

## StreamChatUI
### βœ… Added
- Open `shouldMarkThreadRead` and `shouldMarkChannelRead` [#3468](https://github.com/GetStream/stream-chat-swift/pull/3468)
### 🐞 Fixed
- Fix channel list state views not updating when the view is not visible [#3479](https://github.com/GetStream/stream-chat-swift/pull/3479)

# [4.65.0](https://github.com/GetStream/stream-chat-swift/releases/tag/4.65.0)
_October 18, 2024_

Expand Down
15 changes: 11 additions & 4 deletions DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -408,10 +408,8 @@ final class DemoChatChannelListRouter: ChatChannelListRouter {
}
}),
.init(title: "Show Channel Info", handler: { [unowned self] _ in
self.rootViewController.presentAlert(
title: "Channel Info",
message: channelController.channel.debugDescription
)
let debugViewController = DebugObjectViewController(object: channelController.channel)
self.rootViewController.present(debugViewController, animated: true)
}),
.init(title: "Show Channel Members", handler: { [unowned self] _ in
guard let cid = channelController.channel?.cid else { return }
Expand Down Expand Up @@ -482,6 +480,15 @@ final class DemoChatChannelListRouter: ChatChannelListRouter {
channelController.createNewMessage(text: message, skipPush: true)
}
}),
.init(title: "Send system message", isEnabled: canSendMessage, handler: { [unowned self] _ in
self.rootViewController.presentAlert(title: "Enter the message text", textFieldPlaceholder: "Send message") { message in
guard let message = message, !message.isEmpty else {
self.rootViewController.presentAlert(title: "Message is not valid")
return
}
channelController.createSystemMessage(text: message)
}
}),
.init(title: "Send message without url enriching", isEnabled: canSendMessage, handler: { [unowned self] _ in
self.rootViewController.presentAlert(title: "Enter the message text", textFieldPlaceholder: "Send message") { message in
guard let message = message, !message.isEmpty else {
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ GEM
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.3.8)
rexml (3.3.9)
rouge (2.0.7)
rubocop (1.38.0)
json (~> 2.3)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ public class ChannelConfig: Codable {
case commands
case createdAt = "created_at"
case updatedAt = "updated_at"
case skipLastMsgAtUpdateForSystemMsg = "skip_last_msg_update_for_system_msgs"
}

/// If users are allowed to add reactions to messages. Enabled by default.
Expand Down Expand Up @@ -258,6 +259,8 @@ public class ChannelConfig: Codable {
public let updatedAt: Date
/// Determines if polls are enabled.
public let pollsEnabled: Bool
/// Determines if system messages should not update the last message at date.
public let skipLastMsgAtUpdateForSystemMsg: Bool

public required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
Expand All @@ -282,6 +285,7 @@ public class ChannelConfig: Codable {
createdAt = try container.decode(Date.self, forKey: .createdAt)
updatedAt = try container.decode(Date.self, forKey: .updatedAt)
pollsEnabled = try container.decodeIfPresent(Bool.self, forKey: .pollsEnabled) ?? false
skipLastMsgAtUpdateForSystemMsg = try container.decodeIfPresent(Bool.self, forKey: .skipLastMsgAtUpdateForSystemMsg) ?? false
}

internal required init(
Expand All @@ -296,6 +300,7 @@ public class ChannelConfig: Codable {
mutesEnabled: Bool = false,
pollsEnabled: Bool = false,
urlEnrichmentEnabled: Bool = false,
skipLastMsgAtUpdateForSystemMsg: Bool = false,
messageRetention: String = "",
maxMessageLength: Int = 0,
commands: [Command] = [],
Expand All @@ -318,5 +323,6 @@ public class ChannelConfig: Codable {
self.createdAt = createdAt
self.updatedAt = updatedAt
self.pollsEnabled = pollsEnabled
self.skipLastMsgAtUpdateForSystemMsg = skipLastMsgAtUpdateForSystemMsg
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ struct MessageRequestBody: Encodable {
let id: String
let user: UserRequestBody
let text: String

// Used at the moment only for creating a system a message.
let type: String?

let command: String?
let args: String?
let parentId: String?
Expand All @@ -269,6 +273,7 @@ struct MessageRequestBody: Encodable {
id: String,
user: UserRequestBody,
text: String,
type: String? = nil,
command: String? = nil,
args: String? = nil,
parentId: String? = nil,
Expand All @@ -285,6 +290,7 @@ struct MessageRequestBody: Encodable {
self.id = id
self.user = user
self.text = text
self.type = type
self.command = command
self.args = args
self.parentId = parentId
Expand Down Expand Up @@ -312,6 +318,7 @@ struct MessageRequestBody: Encodable {
try container.encodeIfPresent(pinExpires, forKey: .pinExpires)
try container.encode(isSilent, forKey: .isSilent)
try container.encodeIfPresent(pollId, forKey: .pollId)
try container.encodeIfPresent(type, forKey: .type)

if !attachments.isEmpty {
try container.encode(attachments, forKey: .attachments)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,53 @@ public class ChatChannelController: DataController, DelegateCallable, DataStoreP
completion: completion
)
}


/// Sends a system message to the channel.
///
/// - Parameters:
/// - text: The text of the system message.
/// - messageId: The id for the sent message. By default, it is automatically generated by Stream.
/// - extraData: The extra data for the message.
/// - completion: Called when saving the message to the local DB finishes.
public func createSystemMessage(
text: String,
messageId: MessageId? = nil,
extraData: [String: RawJSON] = [:],
completion: ((Result<MessageId, Error>) -> Void)? = nil
) {
guard let cid = cid, isChannelAlreadyCreated else {
channelModificationFailed { error in
completion?(.failure(error ?? ClientError.Unknown()))
}
return
}

updater.createNewMessage(
in: cid,
messageId: messageId,
text: text,
pinning: nil,
isSilent: false,
isSystem: true,
command: nil,
arguments: nil,
attachments: [],
mentionedUserIds: [],
quotedMessageId: nil,
skipPush: false,
skipEnrichUrl: false,
poll: nil,
extraData: extraData
) { result in
if let newMessage = try? result.get() {
self.client.eventNotificationCenter.process(NewMessagePendingEvent(message: newMessage))
}
self.callback {
completion?(result.map(\.id))
}
}
}

/// Creates a new poll.
///
/// - Parameters:
Expand Down Expand Up @@ -1285,6 +1331,7 @@ public class ChatChannelController: DataController, DelegateCallable, DataStoreP
text: text,
pinning: pinning,
isSilent: isSilent,
isSystem: false,
command: nil,
arguments: nil,
attachments: attachments,
Expand Down
3 changes: 3 additions & 0 deletions Sources/StreamChat/Database/DTOs/ChannelConfigDTO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ final class ChannelConfigDTO: NSManagedObject {
@NSManaged var typingEventsEnabled: Bool
@NSManaged var readEventsEnabled: Bool
@NSManaged var connectEventsEnabled: Bool
@NSManaged var skipLastMsgAtUpdateForSystemMsg: Bool
@NSManaged var uploadsEnabled: Bool
@NSManaged var repliesEnabled: Bool
@NSManaged var quotesEnabled: Bool
Expand All @@ -37,6 +38,7 @@ final class ChannelConfigDTO: NSManagedObject {
mutesEnabled: mutesEnabled,
pollsEnabled: pollsEnabled,
urlEnrichmentEnabled: urlEnrichmentEnabled,
skipLastMsgAtUpdateForSystemMsg: skipLastMsgAtUpdateForSystemMsg,
messageRetention: messageRetention,
maxMessageLength: Int(maxMessageLength),
commands: Array(Set(
Expand Down Expand Up @@ -76,6 +78,7 @@ extension ChannelConfig {
dto.updatedAt = updatedAt.bridgeDate
dto.commands = NSOrderedSet(array: commands.map { $0.asDTO(context: context) })
dto.pollsEnabled = pollsEnabled
dto.skipLastMsgAtUpdateForSystemMsg = skipLastMsgAtUpdateForSystemMsg
return dto
}
}
33 changes: 27 additions & 6 deletions Sources/StreamChat/Database/DTOs/MessageDTO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class MessageDTO: NSManagedObject {
@NSManaged var replyCount: Int32
@NSManaged var extraData: Data?
@NSManaged var isSilent: Bool

@NSManaged var skipPush: Bool
@NSManaged var skipEnrichUrl: Bool
@NSManaged var isShadowed: Bool
Expand Down Expand Up @@ -621,6 +622,7 @@ extension NSManagedObjectContext: MessageDatabaseSession {
mentionedUserIds: [UserId],
showReplyInChannel: Bool,
isSilent: Bool,
isSystem: Bool,
quotedMessageId: MessageId?,
createdAt: Date?,
skipPush: Bool,
Expand Down Expand Up @@ -654,7 +656,6 @@ extension NSManagedObjectContext: MessageDatabaseSession {
}

message.cid = cid.rawValue
message.type = parentMessageId == nil ? MessageType.regular.rawValue : MessageType.reply.rawValue
message.text = text
message.command = command
message.args = arguments
Expand All @@ -666,7 +667,16 @@ extension NSManagedObjectContext: MessageDatabaseSession {
message.reactionScores = [:]
message.reactionCounts = [:]
message.reactionGroups = []


// Message type
if parentMessageId != nil {
message.type = MessageType.reply.rawValue
} else if isSystem {
message.type = MessageType.system.rawValue
} else {
message.type = MessageType.regular.rawValue
}

if let poll {
message.poll = try? savePoll(payload: poll, cache: nil)
}
Expand All @@ -686,9 +696,12 @@ extension NSManagedObjectContext: MessageDatabaseSession {
message.user = currentUserDTO.user
message.channel = channelDTO

let newLastMessageAt = max(channelDTO.lastMessageAt?.bridgeDate ?? createdAt, createdAt).bridgeDate
channelDTO.lastMessageAt = newLastMessageAt
channelDTO.defaultSortingAt = newLastMessageAt
let shouldNotUpdateLastMessageAt = isSystem && channelDTO.config.skipLastMsgAtUpdateForSystemMsg
if !shouldNotUpdateLastMessageAt {
let newLastMessageAt = max(channelDTO.lastMessageAt?.bridgeDate ?? createdAt, createdAt).bridgeDate
channelDTO.lastMessageAt = newLastMessageAt
channelDTO.defaultSortingAt = newLastMessageAt
}

if let parentMessageId = parentMessageId,
let parentMessageDTO = MessageDTO.load(id: parentMessageId, context: self) {
Expand Down Expand Up @@ -813,7 +826,11 @@ extension NSManagedObjectContext: MessageDatabaseSession {
array: payload.threadParticipants.map { try saveUser(payload: $0) }
)

channelDTO.lastMessageAt = max(channelDTO.lastMessageAt?.bridgeDate ?? payload.createdAt, payload.createdAt).bridgeDate
let isSystemMessage = dto.type == MessageType.system.rawValue
let shouldNotUpdateLastMessageAt = isSystemMessage && channelDTO.config.skipLastMsgAtUpdateForSystemMsg
if !shouldNotUpdateLastMessageAt {
channelDTO.lastMessageAt = max(channelDTO.lastMessageAt?.bridgeDate ?? payload.createdAt, payload.createdAt).bridgeDate
}

dto.channel = channelDTO

Expand Down Expand Up @@ -1241,10 +1258,14 @@ extension MessageDTO {
.sorted { ($0.attachmentID?.index ?? 0) < ($1.attachmentID?.index ?? 0) }
.compactMap { $0.asRequestPayload() }

// At the moment, we only provide the type for system messages when creating a message.
let systemType = type == MessageType.system.rawValue ? type : nil

return .init(
id: id,
user: user.asRequestBody(),
text: text,
type: systemType,
command: command,
args: args,
parentId: parentMessageId,
Expand Down
3 changes: 3 additions & 0 deletions Sources/StreamChat/Database/DatabaseSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ protocol MessageDatabaseSession {
mentionedUserIds: [UserId],
showReplyInChannel: Bool,
isSilent: Bool,
isSystem: Bool,
quotedMessageId: MessageId?,
createdAt: Date?,
skipPush: Bool,
Expand Down Expand Up @@ -220,6 +221,7 @@ extension MessageDatabaseSession {
pinning: MessagePinning?,
quotedMessageId: MessageId?,
isSilent: Bool = false,
isSystem: Bool,
skipPush: Bool,
skipEnrichUrl: Bool,
attachments: [AnyAttachmentPayload] = [],
Expand All @@ -239,6 +241,7 @@ extension MessageDatabaseSession {
mentionedUserIds: mentionedUserIds,
showReplyInChannel: false,
isSilent: isSilent,
isSystem: isSystem,
quotedMessageId: quotedMessageId,
createdAt: nil,
skipPush: skipPush,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23231" systemVersion="24A348" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23507" systemVersion="23G93" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="AttachmentDTO" representedClassName="AttachmentDTO" syncable="YES">
<attribute name="data" attributeType="Binary"/>
<attribute name="id" attributeType="String"/>
Expand Down Expand Up @@ -28,6 +28,7 @@
<attribute name="readEventsEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="repliesEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="searchEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="skipLastMsgAtUpdateForSystemMsg" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="typingEventsEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="uploadsEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ import Foundation

extension SystemEnvironment {
/// A Stream Chat version.
public static let version: String = "4.65.0"
public static let version: String = "4.66.0"
}
2 changes: 1 addition & 1 deletion Sources/StreamChat/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>4.65.0</string>
<string>4.66.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
Expand Down
Loading
Loading