-
Notifications
You must be signed in to change notification settings - Fork 101
Introduce MessageViewModel
+ Show original translated message
#815
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
base: develop
Are you sure you want to change the base?
Conversation
Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageContainerView.swift
Show resolved
Hide resolved
SDK Size
|
Generated by 🚫 Danger |
Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageContainerView.swift
Outdated
Show resolved
Hide resolved
StreamChatSwiftUITests/Tests/ChatChannel/MessageListViewAvatars_Tests.swift
Show resolved
Hide resolved
Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageViewModel.swift
Show resolved
Hide resolved
public struct LinkDetectionTextView: View { | ||
@Environment(\.layoutDirection) var layoutDirection | ||
@Environment(\.channelTranslationLanguage) var translationLanguage | ||
|
||
@EnvironmentObject private var viewModel: MessageViewModel |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is a good example that @EnvironmentObject is not a good idea here, since LinkDetectionTextView
is likely to be reused as a standalone by other customers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels like we should go for @StateObject even when it requires making some inits deprecated. Maybe it is time to create a new text view instead? RichTextView or something since the naming does not reflect anymore what it does (does much more than links). New type would allow making bigger changes.
.onAppear { | ||
displayedText = attributedString(for: message) | ||
} | ||
.onChange(of: message, perform: { updated in | ||
displayedText = attributedString(for: updated) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will probably need some help here CC @martinmitrevski @laevandus. This was overriding the view model content always, and was causing some trouble to me. Why is this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to see not having it here. To be honest, I am not sure why it was needed in the first place.
Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageView.swift
Outdated
Show resolved
Hide resolved
if let displayedText { | ||
Text(displayedText) | ||
} else { | ||
Text(message.adjustedText) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In case of LinkDetectionTextView
, I'm considering passing a new property, maybe originalText: String?
instead of the whole view model, so that it requires less changes. Still need to check if it would work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's a simpler way, without using environment object and involving the channel view model.
- we pass the message view model in the MessageContainerView, with default value. We can do that, it's not a breaking change.
- we introduce a state there for each message, whether the show original is tapped for that message.
- customers will implement the existing makeMessageContainer method, by passing their own VM to the regular view.
Let's discuss it when you are back, but I think we can simplify things here a bit.
e3a800d
to
f890c13
Compare
|
private struct MessageViewModelKey: EnvironmentKey { | ||
static let defaultValue: MessageViewModel? = nil | ||
} | ||
|
||
private struct ChatChannelViewModelKey: EnvironmentKey { | ||
static let defaultValue: ChatChannelViewModel? = nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These seems to be unused now
🔗 Issue Links
https://linear.app/stream/issue/IOS-794/show-original-translation
https://linear.app/stream/issue/IOS-823/customizing-message-view-display-logic
🎯 Goal
📝 Summary
Goal
The main goal of this PR is to introduce the new
MessageViewModel
which was required to properly implement the original translated message feature without breaking changes or too many hacks. Besides this, a lot of customers want to override the logic of the Message View, especially when to hide or show some views based on business logic.Requirement
One thing that was important to do was to have a way to share state from the
ChatChannelViewModel
to the newMessageViewModel
, since theMessageViewModel
should not have any internal state to not cause unnecessary re-renders. Besides that, since the message view is inside a LazyStack, the state would be overidden whenever the view is recreated. So it is important to provide the message view model to each message view whenever it is created.🛠 Implementation
Solution
The most common practice is to have item view models as the data of the
ListViewModel
. So instead ofmessages: [ChatMessage]
as the data, it would bemessages: [MessageViewModel]
. But because of ourLazyCachedMapCollection
, and to avoid breaking changes, we can't really do this.The final solution is to create a factory method in the
ChatChannelViewModel
, calledmakeMessageViewModel(message:)
. This approach allows us to pass data from the channel view model to the message view model, ensuring the view model is always updated whenever the view is re-created. The view model is then passed to theMessageContainerView
as an environment object. (Can't be @ObservedObject, otherwise it would be breaking)In order to provide a custom
MessageViewModel
, customers will need to subclass theMessageViewModel
and override the functionChatChannelViewModel.makeMessageViewModel()
.🧪 Manual Testing Notes
TODO
☑️ Contributor Checklist
docs-content
repo