Skip to content

Add Draft Messages Support #775

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

Merged
merged 26 commits into from
Mar 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
fab0d80
WIP w/ channel view model
nuno-vieira Mar 3, 2025
8ddbf9e
WIP without channel view model env object
nuno-vieira Mar 3, 2025
4043f06
Fill the draft attachments to the composer
nuno-vieira Mar 5, 2025
6e93ef9
Fix commands not showing in draft message
nuno-vieira Mar 5, 2025
a0154f2
Add draft previews to channel list item and thread list item
nuno-vieira Mar 6, 2025
d136a4e
Delete draft message when publishing message or erasing input
nuno-vieira Mar 6, 2025
dc51245
Add draftMessagesEnabled flag
nuno-vieira Mar 6, 2025
9789941
Update composer draft message when draft is updated from event
nuno-vieira Mar 6, 2025
4829f77
Fix forgotten env object from channel view model
nuno-vieira Mar 6, 2025
ce87d7e
Remove unnecessary deleteDraftMessage call since publishing a message…
nuno-vieira Mar 6, 2025
f57903a
Simplify the composer view model draft update event logic
nuno-vieira Mar 6, 2025
47c0a02
Add Message Composer View Model Tests
nuno-vieira Mar 6, 2025
23f57c5
Add composer view test coverage
nuno-vieira Mar 7, 2025
ff442ca
Improve the composer view model code by having a fillDraftMessage and…
nuno-vieira Mar 7, 2025
5975ef6
Add test coverage to previews
nuno-vieira Mar 7, 2025
76cb87c
Update CHANGELOG.md
nuno-vieira Mar 7, 2025
a1fab89
Merge branch 'develop' into add/drafts-support
nuno-vieira Mar 7, 2025
767eedb
Fix snapshot tests
nuno-vieira Mar 10, 2025
fdd6e64
Merge branch 'develop' into add/drafts-support
nuno-vieira Mar 13, 2025
df6ee32
Fix broken compilation caused by new formatter
nuno-vieira Mar 13, 2025
5fd0699
Merge branch 'develop' into add/drafts-support
testableapple Mar 24, 2025
84168e5
Update CHANGELOG.md
nuno-vieira Mar 24, 2025
c7a1158
Clear composer input data if draft was deleted from other device
nuno-vieira Mar 24, 2025
963141c
Fix delete draft request being fired multiple times
nuno-vieira Mar 25, 2025
3593335
Fix not removing attachments from the previous draft
nuno-vieira Mar 25, 2025
0eccc10
Only delete draft message if content is all empty
nuno-vieira Mar 25, 2025
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: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### ✅ Added
- Add avatar customization in add users popup [#787](https://github.com/GetStream/stream-chat-swiftui/pull/787)
- Add support for Draft Messages when `Utils.messageListConfig.draftMessagesEnabled` is `true` [#775](https://github.com/GetStream/stream-chat-swiftui/pull/775)
- Add draft preview in Channel List and Thread List if drafts are enabled [#775](https://github.com/GetStream/stream-chat-swiftui/pull/775)

# [4.74.0](https://github.com/GetStream/stream-chat-swiftui/releases/tag/4.74.0)
_March 14, 2025_
Expand Down
3 changes: 2 additions & 1 deletion DemoAppSwiftUI/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ class AppDelegate: NSObject, UIApplicationDelegate {
bouncedMessagesAlertActionsEnabled: true,
skipEditedMessageLabel: { message in
message.extraData["ai_generated"]?.boolValue == true
}
},
draftMessagesEnabled: true
),
composerConfig: ComposerConfig(isVoiceRecordingEnabled: true)
)
Expand Down
3 changes: 2 additions & 1 deletion DemoAppSwiftUI/AppleMessageComposerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ struct AppleMessageComposerView<Factory: ViewFactory>: View, KeyboardReadable {
channelConfig = channelController.channel?.config
let vm = viewModel ?? ViewModelsFactory.makeMessageComposerViewModel(
with: channelController,
messageController: messageController
messageController: messageController,
quotedMessage: quotedMessage
)
_viewModel = StateObject(
wrappedValue: vm
Expand Down
11 changes: 10 additions & 1 deletion DemoAppSwiftUI/PinChannelHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,16 @@ struct DemoAppChatChannelListItem: View {
TypingIndicatorView()
}
}
SubtitleText(text: injectedChannelInfo?.subtitle ?? channel.subtitleText)
if let draftText = channel.draftMessageText {
HStack(spacing: 2) {
Text("Draft:")
.font(fonts.caption1).bold()
.foregroundColor(Color(colors.highlightedAccentBackground))
SubtitleText(text: draftText)
}
} else {
SubtitleText(text: injectedChannelInfo?.subtitle ?? channel.subtitleText)
}
Spacer()
}
.accessibilityIdentifier("subtitleView")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright © 2025 Stream.io Inc. All rights reserved.
//

import AVFoundation
import StreamChat
import SwiftUI

Expand Down Expand Up @@ -52,6 +53,48 @@ extension AddedAsset {
}
}

extension AnyChatMessageAttachment {
func toAddedAsset() -> AddedAsset? {
if let imageAttachment = attachment(payloadType: ImageAttachmentPayload.self),
let imageData = try? Data(contentsOf: imageAttachment.imageURL),
let image = UIImage(data: imageData) {
return AddedAsset(
image: image,
id: imageAttachment.id.rawValue,
url: imageAttachment.imageURL,
type: .image,
extraData: imageAttachment.extraData ?? [:]
)
} else if let videoAttachment = attachment(payloadType: VideoAttachmentPayload.self),
let thumbnail = imageThumbnail(for: videoAttachment.payload) {
return AddedAsset(
image: thumbnail,
id: videoAttachment.id.rawValue,
url: videoAttachment.videoURL,
type: .video,
extraData: videoAttachment.extraData ?? [:]
)
}
return nil
}

private func imageThumbnail(for videoAttachmentPayload: VideoAttachmentPayload) -> UIImage? {
if let thumbnailURL = videoAttachmentPayload.thumbnailURL, let data = try? Data(contentsOf: thumbnailURL) {
return UIImage(data: data)
}
let asset = AVURLAsset(url: videoAttachmentPayload.videoURL, options: nil)
let imageGenerator = AVAssetImageGenerator(asset: asset)
imageGenerator.appliesPreferredTrackTransform = true
guard let cgImage = try? imageGenerator.copyCGImage(
at: CMTimeMake(value: 0, timescale: 1),
actualTime: nil
) else {
return nil
}
return UIImage(cgImage: cgImage)
}
}

/// Type of asset added to the composer.
public enum AssetType {
case image
Expand Down Expand Up @@ -92,3 +135,16 @@ public struct AddedVoiceRecording: Identifiable, Equatable {
self.waveform = waveform
}
}

extension AnyChatMessageAttachment {
func toAddedVoiceRecording() -> AddedVoiceRecording? {
guard let voiceAttachment = attachment(payloadType: VoiceRecordingAttachmentPayload.self) else { return nil }
guard let duration = voiceAttachment.duration else { return nil }
guard let waveform = voiceAttachment.waveformData else { return nil }
return AddedVoiceRecording(
url: voiceAttachment.voiceRecordingURL,
duration: duration,
waveform: waveform
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public struct MessageComposerView<Factory: ViewFactory>: View, KeyboardReadable
private var channelConfig: ChannelConfig?
@Binding var quotedMessage: ChatMessage?
@Binding var editedMessage: ChatMessage?

private let recordingViewHeight: CGFloat = 80

public init(
Expand All @@ -37,7 +37,8 @@ public struct MessageComposerView<Factory: ViewFactory>: View, KeyboardReadable
_viewModel = StateObject(
wrappedValue: viewModel ?? ViewModelsFactory.makeMessageComposerViewModel(
with: channelController,
messageController: messageController
messageController: messageController,
quotedMessage: quotedMessage
)
)
_quotedMessage = quotedMessage
Expand Down Expand Up @@ -214,6 +215,12 @@ public struct MessageComposerView<Factory: ViewFactory>: View, KeyboardReadable
viewModel.selectedRangeLocation = editedMessage?.text.count ?? 0
}
}
.onAppear(perform: {
viewModel.fillDraftMessage()
})
.onDisappear(perform: {
viewModel.updateDraftMessage(quotedMessage: quotedMessage)
})
.accessibilityElement(children: .contain)
}
}
Expand Down
Loading
Loading