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.42.0 Release #2888

Merged
merged 17 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
12 changes: 6 additions & 6 deletions .github/workflows/cron-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ jobs:
os: macos-12
device: "iPhone 14 Pro Max"
setup_runtime: false
- ios: 15.5
xcode: 13.4.1
- ios: 15.4
xcode: 14.2
os: macos-12
device: "iPhone 8"
setup_runtime: false
setup_runtime: true
- ios: 14.5
xcode: 14.2
os: macos-12
Expand Down Expand Up @@ -138,11 +138,11 @@ jobs:
os: macos-13
device: "iPhone 14 Pro Max"
setup_runtime: false
- ios: 15.5
xcode: 13.4.1
- ios: 15.4
xcode: 14.2
os: macos-12
device: "iPhone 8"
setup_runtime: false
setup_runtime: true
- ios: 14.5
xcode: 14.2
os: macos-12
Expand Down
24 changes: 16 additions & 8 deletions .github/workflows/smoke-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ on:
- '!main'

workflow_dispatch:
inputs:
snapshots:
description: 'Should Snapshots be recorded on CI?'
type: boolean
required: false
default: false

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand All @@ -23,7 +29,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_PR_NUM: ${{ github.event.number }}
if: ${{ github.event_name != 'push' }}
if: ${{ github.event_name != 'push' && github.event.inputs.snapshots != 'true' }}
steps:
- uses: actions/[email protected]
with:
Expand Down Expand Up @@ -61,6 +67,7 @@ jobs:
test-llc-debug:
name: Test LLC (Debug)
runs-on: macos-13
if: ${{ github.event.inputs.snapshots != 'true' }}
needs: build-test-app-and-frameworks
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down Expand Up @@ -112,10 +119,11 @@ jobs:
path: derived_data/Build/
- uses: ./.github/actions/bootstrap
- name: Run UI Tests (Debug)
run: bundle exec fastlane test_ui device:"${{ env.IOS_SIMULATOR_DEVICE }}" skip_build:true
run: bundle exec fastlane test_ui device:"${{ env.IOS_SIMULATOR_DEVICE }}" skip_build:true record:${{ github.event.inputs.snapshots }}
timeout-minutes: 25
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.CI_BOT_GITHUB_TOKEN }} # to open a PR
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # to use github cli
GITHUB_PR_NUM: ${{ github.event.number }}
- name: Parse xcresult
if: failure()
Expand All @@ -132,7 +140,7 @@ jobs:
test-e2e-debug:
name: Test E2E UI (Debug)
runs-on: macos-12 # TODO: bump macos version to 13 when Xcode 15.0 is released
if: ${{ github.event_name != 'push' }}
if: ${{ github.event_name != 'push' && github.event.inputs.snapshots != 'true' }}
needs:
- allure_testops_launch
- build-test-app-and-frameworks
Expand Down Expand Up @@ -189,7 +197,7 @@ jobs:
allure_testops_launch:
name: Launch Allure TestOps
runs-on: macos-13
if: ${{ github.event_name != 'push' }}
if: ${{ github.event_name != 'push' && github.event.inputs.snapshots != 'true' }}
outputs:
launch_id: ${{ steps.get_launch_id.outputs.launch_id }}
steps:
Expand All @@ -208,7 +216,7 @@ jobs:
build-xcode14:
name: Build LLC + UI (Xcode 14)
runs-on: macos-12
if: ${{ github.event_name != 'push' }}
if: ${{ github.event_name != 'push' && github.event.inputs.snapshots != 'true' }}
env:
XCODE_VERSION: "14.0.1"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -229,7 +237,7 @@ jobs:
name: Build Demo App + Example Apps
runs-on: macos-13
needs: build-test-app-and-frameworks
if: ${{ github.event_name != 'push' }}
if: ${{ github.event_name != 'push' && github.event.inputs.snapshots != 'true' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_PR_NUM: ${{ github.event.number }}
Expand Down Expand Up @@ -261,7 +269,7 @@ jobs:
name: Test Integration
runs-on: macos-13
needs: build-test-app-and-frameworks
if: ${{ github.event_name != 'push' }}
if: ${{ github.event_name != 'push' && github.event.inputs.snapshots != 'true' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_PR_NUM: ${{ github.event.number }}
Expand Down
1 change: 1 addition & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ excluded:
- Carthage
- Pods
- .build
- spm_cache

disabled_rules:
- large_tuple
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.42.0](https://github.com/GetStream/stream-chat-swift/releases/tag/4.42.0)
_November 14, 2023_

## StreamChat
### 🐞 Fixed
- Fix not able to mark channel read after clearing history [#2867](https://github.com/GetStream/stream-chat-swift/pull/2867)
- Fix pasting images from browser when isPastingImagesEnabled is false [#2874](https://github.com/GetStream/stream-chat-swift/pull/2874)
- Fix not being able to paste images when multiple attachments are present [#2874](https://github.com/GetStream/stream-chat-swift/pull/2874)
- Fix ComposerVC InputTextView caret's position issues [#2878](https://github.com/GetStream/stream-chat-swift/pull/2878)
- Fix avatar alignment in quoted messages [#2876](https://github.com/GetStream/stream-chat-swift/pull/2876)

## StreamChatUI
### βœ… Added
- Add support for editing custom attachments [#2879](https://github.com/GetStream/stream-chat-swift/pull/2879)
### 🐞 Fixed
- Fix composer not interactable after enabling send-message capability [#2866](https://github.com/GetStream/stream-chat-swift/pull/2866)

# [4.41.0](https://github.com/GetStream/stream-chat-swift/releases/tag/4.41.0)
_November 03, 2023_

Expand Down
22 changes: 22 additions & 0 deletions Sources/StreamChat/ChatClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ public class ChatClient {
/// The notification center used to send and receive notifications about incoming events.
private(set) var eventNotificationCenter: EventNotificationCenter

/// The registry that contains all the attachment payloads associated with their attachment types.
/// For the meantime this is a static property to avoid breaking changes. On v5, this can be changed.
private(set) static var attachmentTypesRegistry: [AttachmentType: AttachmentPayload.Type] = [
.image: ImageAttachmentPayload.self,
.video: VideoAttachmentPayload.self,
.audio: AudioAttachmentPayload.self,
.file: FileAttachmentPayload.self,
.voiceRecording: VideoAttachmentPayload.self
]

let connectionRepository: ConnectionRepository

let authenticationRepository: AuthenticationRepository
Expand Down Expand Up @@ -235,6 +245,18 @@ public class ChatClient {
)
}

/// Register a custom attachment payload.
///
/// Example:
/// ```
/// registerAttachment(CustomAttachmentPayload.self)
/// ```
///
/// - Parameter payloadType: The payload type of the attachment.
public func registerAttachment<Payload: AttachmentPayload>(_ payloadType: Payload.Type) {
Self.attachmentTypesRegistry[Payload.type] = payloadType
}

/// Connects the client with the given user.
///
/// - Parameters:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1522,6 +1522,13 @@ private extension ChatChannelController {
}

guard let lastReadIndex = messages.firstIndex(where: { $0.id == lastReadMessageId }), lastReadIndex != 0 else {
// If there is a lastReadMessageId, and we loaded all messages, but can't find firstUnreadMessageId,
// then it means the lastReadMessageId is not reachable because the channel was truncated or hidden.
// So we return the oldest regular message already fetched.
if hasLoadedAllPreviousMessages {
return oldestRegularMessage()
}

return nil
}

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.41.0"
public static let version: String = "4.42.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.41.0</string>
<string>4.42.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,21 +193,16 @@ extension AttachmentPayload {
public extension Array where Element == ChatMessageAttachment<Data> {
func toAnyAttachmentPayload() -> [AnyAttachmentPayload] {
compactMap { attachment in
func anyAttachmentPayload<T: Decodable & AttachmentPayload>(for type: T.Type) -> AnyAttachmentPayload? {
guard let payload = try? JSONDecoder.default.decode(T.self, from: attachment.payload) else { return nil }
return AnyAttachmentPayload(payload: payload)
}

switch attachment.type {
case .image: return anyAttachmentPayload(for: ImageAttachmentPayload.self)
case .video: return anyAttachmentPayload(for: VideoAttachmentPayload.self)
case .audio: return anyAttachmentPayload(for: AudioAttachmentPayload.self)
case .file: return anyAttachmentPayload(for: FileAttachmentPayload.self)
case .voiceRecording: return anyAttachmentPayload(for: VoiceRecordingAttachmentPayload.self)
default:
log.assertionFailure("Unsupported attachment")
let types = ChatClient.attachmentTypesRegistry
guard let payloadType = types[attachment.type] else { return nil }
guard let payload = try? JSONDecoder.default.decode(
payloadType,
from: attachment.payload
) else {
return nil
}

return AnyAttachmentPayload(payload: payload)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ open class InputChatMessageView: _View, ComponentsProvider, AppearanceProvider {
if let quotingMessage = content.quotingMessage {
quotedMessageView.content = .init(
message: quotingMessage,
avatarAlignment: .leading,
avatarAlignment: quotingMessage.isSentByCurrentUser ? .trailing : .leading,
channel: content.channel
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ open class InputTextView: UITextView, AppearanceProvider {
}
}

override open var intrinsicContentSize: CGSize {
.init(width: UIView.noIntrinsicMetric, height: minimumHeight)
}

private var oldText: String = ""
private var oldSize: CGSize = .zero
private var shouldScrollAfterHeightChanged = false

override open func didMoveToSuperview() {
super.didMoveToSuperview()
guard superview != nil else { return }
Expand All @@ -67,13 +75,47 @@ open class InputTextView: UITextView, AppearanceProvider {
setUpAppearance()
}

override open func layoutSubviews() {
super.layoutSubviews()

if text == oldText, bounds.size == oldSize { return }
oldText = text
oldSize = bounds.size

let size = sizeThatFits(CGSize(width: bounds.size.width, height: CGFloat.greatestFiniteMagnitude))
var height = size.height

// Constrain minimum height
height = minimumHeight > 0 ? max(height, minimumHeight) : height

// Constrain maximum height
height = maximumHeight > 0 ? min(height, maximumHeight) : height

// Update height constraint if needed
if height != heightConstraint!.constant {
shouldScrollAfterHeightChanged = true
heightConstraint!.constant = height
} else if shouldScrollAfterHeightChanged {
shouldScrollAfterHeightChanged = false
scrollToCaretPosition(animated: true)
}
}

open func setUp() {
contentMode = .redraw

NotificationCenter.default.addObserver(
self,
selector: #selector(handleTextChange),
name: UITextView.textDidChangeNotification,
object: self
)

NotificationCenter.default.addObserver(
self,
selector: #selector(textDidEndEditing),
name: UITextView.textDidEndEditingNotification, object: self
)
}

open func setUpAppearance() {
Expand All @@ -95,17 +137,15 @@ open class InputTextView: UITextView, AppearanceProvider {
}

open func setUpLayout() {
embed(
placeholderLabel,
insets: .init(
top: .zero,
leading: directionalLayoutMargins.leading,
bottom: .zero,
trailing: .zero
)
)
placeholderLabel.pin(anchors: [.centerY], to: self)
placeholderLabel.widthAnchor.pin(equalTo: widthAnchor, multiplier: 0.95).isActive = true
addSubview(placeholderLabel)
NSLayoutConstraint.activate([
placeholderLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: directionalLayoutMargins.leading),
placeholderLabel.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor),
placeholderLabel.topAnchor.constraint(equalTo: topAnchor),
placeholderLabel.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor),

placeholderLabel.centerYAnchor.constraint(equalTo: centerYAnchor)
])

heightConstraint = heightAnchor.pin(equalToConstant: minimumHeight)
heightConstraint?.isActive = true
Expand All @@ -127,17 +167,31 @@ open class InputTextView: UITextView, AppearanceProvider {

open func textDidChangeProgrammatically() {
delegate?.textViewDidChange?(self)
DispatchQueue.main.async {
self.handleTextChange()
}
handleTextChange()
}

@objc open func handleTextChange() {
placeholderLabel.isHidden = !text.isEmpty
setTextViewHeight()
scrollToCaretPosition(animated: true)
setNeedsLayout()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
// This is due to bug in UITextView where the scroll sometimes disables
// when a very long text is pasted in it.
// Doing this ensures that it doesn't happen
// Reference: https://stackoverflow.com/a/62386088/5493299

self?.isScrollEnabled = false
self?.layoutIfNeeded()
self?.isScrollEnabled = true
}
}

@objc func textDidEndEditing(notification: Notification) {
if let sender = notification.object as? InputTextView, sender == self {
scrollToCaretPosition(animated: true)
}
}

@available(*, deprecated, message: "The calculations made by this method are now happening in a more consistent way inside layoutSubviews. This method is not being used now.")
open func setTextViewHeight() {
var heightToSet = minimumHeight

Expand Down Expand Up @@ -171,14 +225,12 @@ open class InputTextView: UITextView, AppearanceProvider {
}

override open func paste(_ sender: Any?) {
if let pasteboardImage = UIPasteboard.general.image {
if isPastingImagesEnabled, let pasteboardImage = UIPasteboard.general.image {
clipboardAttachmentDelegate?.inputTextView(self, didPasteImage: pasteboardImage)
} else {
super.paste(sender)
// On text paste, textView height will not change automatically
// so we must call this function
setTextViewHeight()
}
setNeedsDisplay()
}

/// Scrolls the text view to to the caret's position.
Expand Down
Loading
Loading