-
Notifications
You must be signed in to change notification settings - Fork 24
feat: improve swipe pt.1 - WPB-16787 #3019
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: feature/base-improve-swipe-WPB-16787
Are you sure you want to change the base?
feat: improve swipe pt.1 - WPB-16787 #3019
Conversation
# Conflicts: # wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/ConfigurationMessageCell/ConversationMessageSectionController.swift
|
||
import Foundation | ||
|
||
public struct MessageModel: Equatable { |
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.
Created UI model to have it's own model and not depend on other layers Models.
Also it was requirement becasue this is WireConversationUI
module where I can't import WireDataModel
project.
As well it helps to have just pure struct which helps be safer with background thread calculations
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.
question: I wonder if we want to move these models to the WireDomain package and have, as such, kind of a centralized place where we retrieve these models from wherever we need them. just concerned about duplicating WireDataModel objects spread a little bit everywhere in new code.
update: these look like more like UIModels so probably best to keep it here.
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 are presentation layer models not domain, for me WireDomain
is not a places for them but if it will be used in other modules and not in WireConversationUI
only we'd need to find proper place to put
public enum SystemMessageTypeModel: Int, Equatable { | ||
case performedCall | ||
case missedCall | ||
case messageDeletedForEveryone | ||
} | ||
|
||
public enum DeliveryStateModel: Int, Sendable, Equatable, CaseIterable { | ||
case invalid | ||
case pending | ||
case sent | ||
case delivered | ||
case read | ||
case failedToSend | ||
} | ||
|
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'll split them into its own files later
// private func startRandomStateTimer() { | ||
// timer = Timer.publish(every: 1.0, on: .main, in: .common) | ||
// .autoconnect() | ||
// .sink { [weak self] _ in | ||
// guard let self else { return } | ||
// let (newText, lines) = self.randomMultilineText() | ||
// if lines >= 2 { | ||
// self.significantChangeSubject.send(()) | ||
// } else { | ||
// self.text = newText | ||
// } | ||
// } | ||
// } | ||
|
||
// func randomMultilineText() -> (String, Int) { | ||
// let lines = [ |
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.
Removed in next PR
final class ConversationCellsPreview: UITableViewController { | ||
|
||
enum SectionIdentifier { | ||
case single | ||
} | ||
|
||
typealias ItemIdentifier = ConversationCellModel |
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've commented this out as it was not straight forward to make all models support identifiable for diffable data source. Will try to back that in on final stages
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.
There was typo in Resouces
instead of Resources
var message: ZMConversationMessage | ||
var deliveryState: ZMDeliveryState | ||
var message: MessageModel | ||
var deliveryState: DeliveryStateModel |
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.
Use UI model which is pure structs so we don't have issues with multithreaded access
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.
thought: I don't think we have a convention on that yet however to be more specific about the intent of these models we could rename them with the suffix UIModel
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.
agree, I'll ask in the channel about naming and will rename in next PRs accordingly
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.
Will update in next PRs to avoid back and force merging
extension NewTextCellDescription: NewCellDescription {} | ||
extension BurstTimestampSenderMessageCellDescription: NewCellDescription {} | ||
|
||
final class NewTextCellDescription: ConversationMessageCellDescription { |
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.
New description for first step migration of text message into new way. It will support for now sender, status and just simple message content, without links, previews, search queries, quoted messages
} | ||
} | ||
|
||
final class NewTextCell: UIView, ConversationMessageCell { |
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.
Not used but needed to compile with old approach
final class SenderObserver: NSObject, UserObserving, SenderObserverProtocol { | ||
|
||
var observation: Any? | ||
|
||
var author: String? | ||
private let authorChangedSubject = PassthroughSubject<String, Never>() | ||
var authorChangedPublisher: AnyPublisher<String, Never> { | ||
authorChangedSubject | ||
.removeDuplicates() | ||
.eraseToAnyPublisher() | ||
} | ||
|
||
init( | ||
messageID: NSManagedObjectID, | ||
viewContext: NSManagedObjectContext | ||
) { | ||
super.init() | ||
viewContext.perform { | ||
let message = try! viewContext.existingObject(with: messageID) as! ZMMessage | ||
self.author = message.senderName | ||
if let sender = message.senderUser { | ||
self.observation = UserChangeInfo.add(observer: self, for: sender, context: viewContext) |
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.
Object that helps be smarter with what to observe for each specific part of the message.
Also it reduces amount of updates if nothing really changed (.removeDuplicates())
_ controller: ConversationMessageSectionController, | ||
didRequestRefreshForMessage message: ZMConversationMessage | ||
) { | ||
guard !message.isText else { return } |
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.
For new approach it would be not needed to call that in case of small changes that affect only cell itself
Test Results2 tests 1 ✅ 12s ⏱️ Results for commit 54d41ad. |
Datadog ReportBranch report: ✅ 0 Failed, 1 Passed, 1 Skipped, 12s Total Time |
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.
Nice work so far, left a couple of comments/questions before approving
public enum State { | ||
case none | ||
case some(MessageSenderViewModel) | ||
} |
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.
question: Can't we get rid of the wrapper and use the view model directly as @ObservedObject by removing the constraint of having it optional ? The view should always be tightly coupled to its view model so no need for an optional here, no?
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'm not sure I got the suggestion right, can you pls elaborate @jullianm ?
For some cases we don't need to show sender so we need some way to tell that, nil or some specific state
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.
Why do we need a view model wrapper , can't we use a state inside the view model like for the MessageStatusViewModel
?
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.
But I have case none
for MessageStatusViewModel
as well for a case when we don't need to show status. It's just happen that status view has 2 more states: fail and callList
, 4 in total. So approach is the same, @jullianm hope that makes more clear now?
@ObservedObject var model: MessageSenderViewModelWrapper | ||
|
||
var body: some View { | ||
switch model.state { |
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.
question: I think here instead of relying on the view model state itself (whether it's optional or not) the view model should always be tightly coupled to its view and provide some state inside that the view would check (i.e model.state..)
@ObservedObject var model: MessageStatusViewModel | ||
|
||
var body: some View { | ||
switch model.state { |
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.
yep like this using the view model state property directly 👍
|
||
struct MessageStatusView: View { | ||
|
||
@ObservedObject var model: MessageStatusViewModel |
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.
@ObservedObject var model: MessageStatusViewModel | |
@ObservedObject var viewModel: MessageStatusViewModel |
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.
will rename in next PRs
|
||
import Foundation | ||
|
||
public struct MessageModel: Equatable { |
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.
question: I wonder if we want to move these models to the WireDomain package and have, as such, kind of a centralized place where we retrieve these models from wherever we need them. just concerned about duplicating WireDataModel objects spread a little bit everywhere in new code.
update: these look like more like UIModels so probably best to keep it here.
public typealias ContentView = TextMessageView | ||
|
||
@ObservedObject var senderViewModelWrapper: MessageSenderViewModelWrapper | ||
@ObservedObject var statusViewModel: MessageStatusViewModel |
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.
thought: not a big fan of using these view models inside another view model as, for me, a view should have ideally only one view model although I'm assuming the code here will evolve in next PRs and probably these's no strict rule about this
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.
For me it's really good way to split things up and reuse. later we'd have another message types like assets and with separate sender and status it would easier to just add it to message content and that's it.
This is similar what we have with cell descriptions, we have each for status, sender, reactions, etc. But with VM's it's more self-sufficient in terms if reactive change
|
||
import Foundation | ||
|
||
public extension DateFormatter { |
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.
suggestion: this could go to WireFoundation/Utilities so we could reuse it from any packages
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.
Can't move to WireUtilities
because it's a project. But I think I can move it to WireFoundation
in next PR. However I don't have clear understanding of separation and what should go in e and what in WireDesign
or WireReusableComponents
var message: ZMConversationMessage | ||
var deliveryState: ZMDeliveryState | ||
var message: MessageModel | ||
var deliveryState: DeliveryStateModel |
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.
thought: I don't think we have a convention on that yet however to be more specific about the intent of these models we could rename them with the suffix UIModel
shouldShowSender: Bool, | ||
shouldShowStatus: Bool | ||
) -> TextMessageViewModel | ||
} |
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.
praise: nice, I think we could use Needle foundation which acts as factories in next PRs
This PR is stale because it has been open 30 days with no activity. Please update it or close it in case is not relevant anymore. |
Issue
First part of the planned changes. Please take a look at the bigger picture and approach, this is the goal of the PR.
PR is targeting into feature branch. You can find this first PR a bit raw, it has some TODO's and dev code but it's too many things affected, we need to start with smth. I'll address all this dev code and TODO in next PRs
In this PR
contetConfigration
for which you don't need to reuse cells. Another reason as that you can't create viewModel empty on the fly, we need ViewModels be created before and passed to a cell.WireAuthenication
but without 3rd party DI which can be done as next transition phaseWireConversationUI
moduleWhat's not in this PR:
Testing
Describe how to test.
Optional: attachments like images, videos, etc.
Checklist
[WPB-XXX]
.UI accessibility checklist
If your PR includes UI changes, please utilize this checklist: