Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1620"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "WireReusableUIComponents"
BuildableName = "WireReusableUIComponents"
BlueprintName = "WireReusableUIComponents"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "WireReusableUIComponents"
BuildableName = "WireReusableUIComponents"
BlueprintName = "WireReusableUIComponents"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
7 changes: 6 additions & 1 deletion WireUI/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ let package = Package(
.library(name: "WireSidebarUI", targets: ["WireSidebarUI"]),
],
dependencies: [
.package(url: "https://github.com/wireapp/Down", exact: "2.3.5"),
.package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.1.0"),
.package(path: "../WireAnalytics"),
.package(name: "WireDomainPackage", path: "../WireDomain"),
Expand Down Expand Up @@ -81,7 +82,11 @@ let package = Package(

.target(
name: "WireReusableUIComponents",
dependencies: ["WireDesign", "WireFoundation"],
dependencies: [
"WireDesign",
"WireFoundation",
.product(name: "Down", package: "Down")
],
plugins: [.plugin(name: "SwiftGenPlugin", package: "WirePlugins")]
),
.target(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,12 @@ import SwiftUI

struct SenderMessageView: View {

@ObservedObject var model: MessageSenderViewModelWrapper
@ObservedObject var model: MessageSenderViewModel

var body: some View {
switch model.state {
case .none:
EmptyView() // nothing when don't need to show sender
case .some(let model):
Text(model.senderAttributed)
.frame(maxWidth: .infinity, alignment: .leading)
.fixedSize(horizontal: false, vertical: true)
.animation(.easeInOut, value: model.senderAttributed)
}
Text(model.senderAttributed)
.frame(maxWidth: .infinity, alignment: .leading)
.fixedSize(horizontal: false, vertical: true)
.animation(.easeInOut, value: model.senderAttributed)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ struct MessageStatusView: View {
switch model.state {
case .none:
EmptyView() // when no need to show status view
case let .sendFailure(_):
case .sendFailure(_):
EmptyView() // will be implemented later
case let .callList(_):
case .callList(_):
EmptyView() // will be implemented later
case let .details(statusDetails):
MessageToolboxView(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public final class MessageStatusViewModel: ObservableObject {
private static func updateState(model: MessageModel) -> State {
let datasource = MessageToolboxDataSource(message: model)
switch datasource.content {
case .none: return .none
case let .sendFailure(string):
return .sendFailure(string)
case let .callList(string):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
//

import Foundation
import WireReusableUIComponents

public struct MessageModel: Equatable {

public let nonce: UUID?
public let sender: UserModel?
public let systemMessageType: SystemMessageTypeModel?
Expand All @@ -29,6 +31,7 @@ public struct MessageModel: Equatable {
public let readReceiptsCount: Int
public let deliveryState: DeliveryStateModel
public let isSent: Bool
public let mentions: [MentionModel]

public init(
nonce: UUID?,
Expand All @@ -40,7 +43,8 @@ public struct MessageModel: Equatable {
conversationType: ConversationTypeModel?,
readReceiptsCount: Int,
deliveryState: DeliveryStateModel,
isSent: Bool
isSent: Bool,
mentions: [MentionModel]
) {
self.nonce = nonce
self.sender = sender
Expand All @@ -52,13 +56,50 @@ public struct MessageModel: Equatable {
self.readReceiptsCount = readReceiptsCount
self.deliveryState = deliveryState
self.isSent = isSent
self.mentions = mentions
}
}

public enum SystemMessageTypeModel: Int, Equatable {
case performedCall
case invalid = 0
case participantsAdded
case failedToAddParticipants
case participantsRemoved
case conversationNameChanged
case connectionRequest // deprecated
case connectionUpdate // deprecated
case missedCall
case newClient
case ignoredClient
case conversationIsSecure
case potentialGap
case decryptionFailed
case decryptionFailedRemoteIdentityChanged
case newConversation
case reactivatedDevice // deprecated: Devices can't be reactivated any longer
case usingNewDevice // deprecated: We don't need inform users about new devices any longer
case messageDeletedForEveryone
case performedCall // deprecated: [WPB-6988] we don't show end call messages any longer.
case teamMemberLeave
case messageTimerUpdate
case readReceiptsEnabled
case readReceiptsDisabled
case readReceiptsOn
case legalHoldEnabled
case legalHoldDisabled
case sessionReset
case decryptionFailedResolved
case domainsStoppedFederating
case conversationIsVerified
case conversationIsDegraded
case mlsMigrationFinalized
case mlsMigrationJoinAfterwards
case mlsMigrationOngoingCall
case mlsMigrationStarted
case mlsMigrationUpdateVersion
case mlsMigrationPotentialGap
case mlsNotSupportedSelfUser
case mlsNotSupportedOtherUser
}

public enum DeliveryStateModel: Int, Sendable, Equatable, CaseIterable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public final class MessageToolboxDataSource {
}

/// The content to display for the message.
public private(set) var content: MessageToolboxContent
public private(set) var content: MessageToolboxContent?

// MARK: - Formatting Properties

Expand All @@ -87,42 +87,34 @@ public final class MessageToolboxDataSource {
/// Creates a toolbox data source for the given message.
public init(message: MessageModel) {
self.message = message
self.content = .details(timestamp: "", status: nil, countdown: "")
_ = shouldUpdateContent()
self.content = updateContent()
}

// MARK: - Content

/// Updates the contents of the message toolbox.
/// - parameter widthConstraint: The width available to rend the toolbox contents.
/// - Returns: A boolean to either update the content of the message toolbox or not
public func shouldUpdateContent() -> Bool {
// Compute the state
let previousContent = content

// Determine the content by priority

public func updateContent() -> MessageToolboxContent? {

// [WPB-6988] removed performed call
if message.systemMessageType == .performedCall {
return false
return nil
}
// 1b) Call list for missed calls
else if message.systemMessageType == .missedCall {
content = .callList(makeCallList())
return .callList(makeCallList())
}
// 2) Failed to send
else if let errorMessage = MessageErrorHelper.errorMessage(message) {
content = .sendFailure(errorMessage)
return .sendFailure(errorMessage)
}

// 3) Timestamp
else {
let (timestamp, status, countdown) = makeDetailsString()
content = .details(timestamp: timestamp, status: status, countdown: countdown)
return .details(timestamp: timestamp, status: status, countdown: countdown)
}

// Only perform the changes if the content did change.
return previousContent != content
}

// MARK: - Details Text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,36 +20,62 @@ import Combine
import SwiftUI
import UIKit
import WireDesign
import WireReusableUIComponents

public struct TextMessageView: ConversationCellContentViewProtocol {

@ObservedObject var model: TextMessageViewModel
@State private var internalWidth: CGFloat

public init(model: TextMessageViewModel) {
public init(model: TextMessageViewModel, contentWidth: CGFloat) {
self.model = model
_internalWidth = State(initialValue: contentWidth)

}

public var body: some View {
VStack(alignment: .leading, spacing: 2) {
SenderMessageView(model: model.senderViewModelWrapper)
HStack(spacing: 0) {
Text(model.text)
.multilineTextAlignment(.center)
.font(.footnote)
.fontWeight(.semibold)
.layoutPriority(1)
if case .some(let senderModel) = model.senderViewModelWrapper.state {
SenderMessageView(model: senderModel)
}
VStack {
LinkInteractionTextViewWrapper(
text: model.text,
accentColor: model.accentColor,
shouldDetectTypes: true,
width: internalWidth
)
.frame(maxWidth: .infinity, alignment: .leading)
}

// .onChange(of: newWidth.rounded(.toNearestOrAwayFromZero))

MessageStatusView(model: model.statusViewModel)
}
.padding(.vertical, 4)
.background(
GeometryReader { proxy in
Color.clear
.onAppear {
// Update only if changed significantly
let measuredWidth = proxy.size.width
if abs(measuredWidth - internalWidth) > 1 {
internalWidth = measuredWidth
}
}
}
)
}
}

// MARK: - Previews

#Preview("Simple") {
let model = TextMessageViewModel(
text: "Test message",
text: "Test message ajfhhkjsdf dsfjk hadsjkfh adskjlhf adjskhf jkasdhfjkl asdhajj dsfsd fsda fasdfasdf",
accentColor: .red,
isObfuscated: false,
mentions: [],
senderViewModelWrapper: .init(state: .some(MessageSenderViewModel(
avatarViewModel: AvatarViewModel(color: .red),
senderModel: UserModel(
Expand All @@ -70,7 +96,7 @@ public struct TextMessageView: ConversationCellContentViewProtocol {
))
)
)
TextMessageView(model: model)
TextMessageView(model: model, contentWidth: 330)
}

extension MessageToolboxState {
Expand Down
Loading
Loading