Skip to content

Commit

Permalink
MOB-1771 - Fixed send image functionality in private and group chats (#…
Browse files Browse the repository at this point in the history
…395)

* Added API to upload remote attachments for domain.
Removed all approach to upload attachments.

* Updated upload request details

* Send images as media embed in group chats

* Adjusted chat input field UI

* Check if user able to send attachments.

* Chat row view refactoring

* Show number of members in community
  • Loading branch information
Oleg-Pecheneg authored Feb 26, 2024
1 parent df5031e commit c12cec0
Show file tree
Hide file tree
Showing 16 changed files with 168 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10999,7 +10999,7 @@
repositoryURL = "https://github.com/Oleg-Pecheneg/push-swift-sdk";
requirement = {
kind = revision;
revision = ff3151a6834db9ca841ee0564979298f4b0df053;
revision = 7aa3a1aeea89acd91dcbefcb0473a2a993a66334;
};
};
C66811F02B47B0F500BDABB0 /* XCRemoteSwiftPackageReference "xmtp-ios" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/Oleg-Pecheneg/push-swift-sdk",
"state" : {
"revision" : "ff3151a6834db9ca841ee0564979298f4b0df053"
"revision" : "7aa3a1aeea89acd91dcbefcb0473a2a993a66334"
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ struct ChatView: View, ViewAnalyticsLogger {
ProgressView()
}
}
.environmentObject(viewModel)
.displayError($viewModel.error)
.background(Color.backgroundMuted2)
.onChange(of: viewModel.keyboardFocused) { keyboardFocused in
Expand All @@ -50,6 +49,9 @@ struct ChatView: View, ViewAnalyticsLogger {
.background(.regularMaterial)
}
}
.environmentObject(viewModel)
.environment(\.analyticsViewName, analyticsName)
.environment(\.analyticsAdditionalProperties, additionalAppearAnalyticParameters)
.onAppear(perform: onAppear)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ final class ChatViewModel: ObservableObject, ViewAnalyticsLogger {
chatState = .loading
setupTitle()
setupPlaceholder()
setupFunctionality()
setIfUserCanSendAttachments()
loadAndShowData()
}

Expand Down Expand Up @@ -258,9 +258,13 @@ private extension ChatViewModel {
}
}

func setupFunctionality() {
func setIfUserCanSendAttachments() {
let isProfileHasDomain = appContext.walletsDataService.wallets.findWithAddress(profile.wallet)?.rrDomain != nil
if isCommunityChat() {
canSendAttachments = featureFlagsService.valueFor(flag: .communityMediaEnabled)
let isCommunityMediaEnabled = featureFlagsService.valueFor(flag: .communityMediaEnabled)
canSendAttachments = isCommunityMediaEnabled && isProfileHasDomain
} else {
canSendAttachments = isProfileHasDomain
}
}

Expand Down Expand Up @@ -899,7 +903,7 @@ extension ChatViewModel: UDFeatureFlagsListener {
switch flag {
case .communityMediaEnabled:
if isCommunityChat() {
canSendAttachments = newValue
setIfUserCanSendAttachments()
reloadCachedMessages()
}
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ struct ExpandableTextEditor: View {
.foregroundStyle(Color.foregroundDefault)
.frame(height: max(40, textEditorHeight))
.scrollContentBackground(.hidden)
.padding(EdgeInsets(top: 0, leading: 8,
.padding(EdgeInsets(top: 4, leading: 8,
bottom: 0, trailing: 8))
.background(focused ? Color.backgroundMuted : Color.backgroundSubtle)
.tint(Color.foregroundAccent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@

import SwiftUI

struct MessageInputView: View {
struct MessageInputView: View, ViewAnalyticsLogger {

@Environment(\.analyticsViewName) var analyticsName
@Environment(\.analyticsAdditionalProperties) var additionalAppearAnalyticParameters
@EnvironmentObject var viewModel: ChatViewModel

let input: Binding<String>
let placeholder: String
@FocusState.Binding var focused: Bool
Expand All @@ -17,30 +21,13 @@ struct MessageInputView: View {

var body: some View {
HStack(alignment: .bottom, spacing: 16) {
Menu {
ForEach(AdditionalAction.allCases.filter { $0.isAvailable }, id: \.self) { action in
Button {
UDVibration.buttonTap.vibrate()
additionalActionCallback(action)
} label: {
Label(action.title, systemImage: action.icon)
}
}
} label: {
Image.plusIcon18
.resizable()
.squareFrame(20)
.padding(EdgeInsets(10))
.foregroundStyle(Color.foregroundSecondary)
}
.onButtonTap {
// logButtonPressedAnalyticEvents(button: action.analyticButton)
if viewModel.canSendAttachments {
additionalActionsView()
}

textFieldView()

if !input.wrappedValue.isEmpty {
Button {
UDVibration.buttonTap.vibrate()
sendCallback()
} label: {
Image.arrowUp24
Expand Down Expand Up @@ -88,11 +75,45 @@ struct MessageInputView: View {
return UnstoppableImagePicker.isCameraAvailable
}
}

var analyticButton: Analytics.Button {
switch self {
case .takePhoto:
return .takePhoto
case .choosePhoto:
return .choosePhoto
}
}
}
}

// MARK: - Private methods
private extension MessageInputView {
@MainActor
@ViewBuilder
func additionalActionsView() -> some View {
Menu {
ForEach(AdditionalAction.allCases.filter { $0.isAvailable }, id: \.self) { action in
Button {
UDVibration.buttonTap.vibrate()
logButtonPressedAnalyticEvents(button: action.analyticButton)
additionalActionCallback(action)
} label: {
Label(action.title, systemImage: action.icon)
}
}
} label: {
Image.plusIcon18
.resizable()
.squareFrame(20)
.padding(EdgeInsets(10))
.foregroundStyle(Color.foregroundSecondary)
}
.onButtonTap {
logButtonPressedAnalyticEvents(button: .chatInputActions)
}
}

@ViewBuilder
func textFieldView() -> some View {
ExpandableTextEditor(text: input,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,28 @@ private extension ChatListChatRowView {

@ViewBuilder
func subtitleView() -> some View {
if let lastMessage = chat.lastMessage {
Text(lastMessageTextFrom(message: lastMessage))
if let subtitle = getSubtitleText() {
Text(subtitle)
.lineLimit(2)
.foregroundStyle(Color.foregroundSecondary)
.font(.currentFont(size: 14))
}
}

func getSubtitleText() -> String? {
if let lastMessage = chat.lastMessage {
return lastMessageTextFrom(message: lastMessage)
} else if case .community(let details) = chat.type {
switch details.type {
case .badge(let badgeDetailedInfo):
let holders = badgeDetailedInfo.usage.holders
let holdersKsString = holders.asFormattedKsString
return String.Constants.pluralNHolders.localized(holdersKsString, holders)
}
}
return nil
}

func lastMessageTextFrom(message: MessagingChatMessageDisplayInfo) -> String {
switch message.type {
case .text(let description):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct SelectableChatRowView: View, ViewAnalyticsLogger {
ChatListChatRowView(chat: chat, joinCommunityCallback: {
joinCommunityCallback?(chat)
})
.padding(.init(horizontal: 12))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,25 @@ extension Endpoint {
)
}

static func uploadRemoteAttachment(for domain: DomainItem,
with timedSignature: PersistedTimedSignature,
body: String) throws -> Endpoint {
// https://profile.ud-staging.com/api/user/aaron.x/attachment
let expires = "\(timedSignature.expires)"
let signature = timedSignature.sign
let headers = [
SignatureComponentHeaders.CodingKeys.domain.rawValue: domain.name,
SignatureComponentHeaders.CodingKeys.expires.rawValue: expires,
SignatureComponentHeaders.CodingKeys.signature.rawValue: signature
]
return Endpoint(
host: NetworkConfig.baseProfileHost,
path: "/profile/user/\(domain.name)/attachment",
queryItems: [],
body: body,
headers: headers
)
}
}

// MARK: - Open methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ extension Analytics {
case copyChatMessageToClipboard
case saveChatImage
case blockUserInGroupChat
case chatInputActions, takePhoto, choosePhoto

// Public profile
case follow, unfollow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ struct MessagingAPIServiceHelper {
return serviceMetadata
}

static func uploadDataToWeb3Storage(_ data: Data,
ofType type: String,
by wallet: HexAddress) async throws -> URL {
let domain = try await MessagingAPIServiceHelper.getAnyDomainItem(for: wallet)
let response = try await NetworkService().uploadRemoteAttachment(for: domain,
base64: data.base64EncodedString(),
type: type)
return response.url
}


enum MessagingHelperError: String, LocalizedError {
case noDomainForWallet
case failedToDecodeServiceData
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,8 @@ private extension PushMessagingAPIService {
receiver: String,
by user: MessagingChatUserProfile) async throws -> Push.PushChat.SendOptions {
let env = getCurrentPushEnvironment()
let pushMessageContent = try getPushMessageContentFrom(displayType: messageType)
let pushMessageContent = try await getPushMessageContentFrom(displayType: messageType,
by: user)
let pushMessageType = try getPushMessageTypeFrom(displayType: messageType)
let pgpPrivateKey = try await getPGPPrivateKeyFor(user: user)
let reference = getPushMessageReferenceFrom(displayType: messageType)
Expand All @@ -668,26 +669,32 @@ private extension PushMessagingAPIService {
try MessagingAPIServiceHelper.decodeServiceMetadata(from: data)
}

func getPushMessageContentFrom(displayType: MessagingChatMessageDisplayType) throws -> String {
func getPushMessageContentFrom(displayType: MessagingChatMessageDisplayType,
by user: MessagingChatUserProfile) async throws -> String {
switch displayType {
case .text(let details):
return details.text
case .imageBase64(let details):
let entity = PushEnvironment.PushMessageContentResponse(content: details.base64)
guard let jsonString = entity.jsonString() else { throw PushMessagingAPIServiceError.failedToPrepareMessageContent }
return jsonString
guard let data = Data(base64Encoded: details.base64) else { throw PushMessagingAPIServiceError.failedToPrepareMessageContent }
return try await getImagePushMessageContentFrom(data: data, by: user)
case .imageData(let details):
guard let base64 = details.image?.base64String else { throw PushMessagingAPIServiceError.unsupportedType }
let preparedBase64 = Base64DataTransformer.addingImageIdentifier(to: base64)
let imageBase64TypeDetails = MessagingChatMessageImageBase64TypeDisplayInfo(base64: preparedBase64)
return try getPushMessageContentFrom(displayType: .imageBase64(imageBase64TypeDetails))
return try await getImagePushMessageContentFrom(data: details.data, by: user)
case .reaction(let details):
return details.content
case .unknown, .remoteContent:
throw PushMessagingAPIServiceError.unsupportedType
}
}

func getImagePushMessageContentFrom(data: Data,
by user: MessagingChatUserProfile) async throws -> String {
let wallet = user.wallet
let url = try await MessagingAPIServiceHelper.uploadDataToWeb3Storage(data,
ofType: "image/png",
by: wallet)
return url.absoluteString
}

func getPushMessageReferenceFrom(displayType: MessagingChatMessageDisplayType) -> String? {
switch displayType {
case .reaction(let details):
Expand All @@ -704,7 +711,7 @@ private extension PushMessagingAPIService {
case .reaction:
return .reaction
case .imageBase64, .imageData:
return .image
return .mediaEmbed
case .unknown, .remoteContent:
throw PushMessagingAPIServiceError.unsupportedType
}
Expand Down
Loading

0 comments on commit c12cec0

Please sign in to comment.