This commit is contained in:
JRoberts
2022-04-12 22:35:09 +04:00
parent 9d84ddc214
commit 1a869460b8
9 changed files with 238 additions and 26 deletions
+12 -7
View File
@@ -775,13 +775,13 @@ struct CIFile: Decodable {
}
}
enum CIFileStatus: Decodable {
case sndStored
case sndCancelled
case rcvInvitation
case rcvTransfer
case rcvComplete
case rcvCancelled
enum CIFileStatus: String, Decodable {
case sndStored = "snd_stored"
case sndCancelled = "snd_cancelled"
case rcvInvitation = "rcv_invitation"
case rcvTransfer = "rcv_transfer"
case rcvComplete = "rcv_complete"
case rcvCancelled = "rcv_cancelled"
}
enum MsgContent {
@@ -819,6 +819,7 @@ enum MsgContent {
case type
case text
case preview
case image
}
}
@@ -836,6 +837,10 @@ extension MsgContent: Decodable {
let text = try container.decode(String.self, forKey: CodingKeys.text)
let preview = try container.decode(LinkPreview.self, forKey: CodingKeys.preview)
self = .link(text: text, preview: preview)
case "image":
let text = try container.decode(String.self, forKey: CodingKeys.text)
let image = try container.decode(String.self, forKey: CodingKeys.image)
self = .image(text: text, image: image)
default:
let text = try? container.decode(String.self, forKey: CodingKeys.text)
self = .unknown(type: type, text: text ?? "unknown message format")
@@ -51,7 +51,9 @@ struct FramedItemView: View {
.frame(minWidth: msgWidth, alignment: .center)
.padding(.bottom, 2)
} else {
if case let .link(_, preview) = chatItem.content.msgContent {
if case let .image(_, image) = chatItem.content.msgContent {
ChatItemImageView(image: image, file: chatItem.file?.filePath)
} else if case let .link(_, preview) = chatItem.content.msgContent {
ChatItemLinkView(linkPreview: preview)
}
MsgContentView(
+38 -3
View File
@@ -24,6 +24,9 @@ struct ChatView: View {
@State private var showChatInfo = false
@State private var showDeleteMessage = false
@State private var chosenImage: UIImage? = nil
@State private var imagePreview: String? = nil
var body: some View {
let cInfo = chat.chatInfo
@@ -90,7 +93,9 @@ struct ChatView: View {
sendMessage: sendMessage,
resetMessage: { message = "" },
inProgress: inProgress,
keyboardVisible: $keyboardVisible
keyboardVisible: $keyboardVisible,
chosenImage: $chosenImage,
imagePreview: $imagePreview
)
}
.navigationTitle(cInfo.chatViewName)
@@ -221,7 +226,13 @@ struct ChatView: View {
}
} else {
let mc: MsgContent
if let preview = linkPreview {
var file: String? = nil
if let preview = imagePreview,
let uiImage = chosenImage,
let savedFile = saveImage(uiImage) {
mc = .image(text: text, image: preview)
file = savedFile
} else if let preview = linkPreview {
mc = .link(text: text, preview: preview)
} else {
mc = .text(text)
@@ -229,7 +240,7 @@ struct ChatView: View {
let chatItem = try await apiSendMessage(
type: chat.chatInfo.chatType,
id: chat.chatInfo.apiId,
file: nil,
file: file,
quotedItemId: quotedItem?.meta.itemId,
msg: mc
)
@@ -244,6 +255,30 @@ struct ChatView: View {
}
}
}
func saveImage(_ uiImage: UIImage) -> String? {
if let imageResized = resizeImageToDataSize(uiImage, maxDataSize: 160000),
let dataResized = Data(base64Encoded: dropImagePrefix(imageResized)),
let jpegData = UIImage(data: dataResized)?.jpegData(compressionQuality: 1) {
let imgName = "IMG_\(Date().timeIntervalSince1970).jpg"
let filename = getDocumentsDirectory().appendingPathComponent(imgName)
do {
try jpegData.write(to: filename)
return filename.path
} catch {
logger.error("ChatView.saveImage error: \(error.localizedDescription)")
return nil
}
}
return nil
}
func getDocumentsDirectory() -> URL {
let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
return path
// let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
// return paths[0]
}
func deleteMessage(_ mode: CIDeleteMode) {
logger.debug("ChatView deleteMessage")
@@ -15,6 +15,24 @@ import SwiftUI
// case editing(editingItem: ChatItem)
//}
//enum ReferencedItem {
// case none
// case quoted(quotedItem: ChatItem)
// case editing(editingItem: ChatItem)
//}
//
//enum Preview {
// case none
// case link(linkPreview: LinkPreview)
// case image(image: UIImage)
//}
//
//struct ComposeState {
// var quotedItem: ChatItem? = nil
// var editingItem: ChatItem? = nil
// var linkPreview: LinkPreview? = nil
//}
struct ComposeView: View {
@Binding var message: String
@Binding var quotedItem: ChatItem?
@@ -31,10 +49,17 @@ struct ComposeView: View {
@State var pendingLinkUrl: URL? = nil
@State var cancelledLinks: Set<String> = []
@State private var showChooseSource = false
@State private var showImagePicker = false
@State private var imageSource: ImageSource = .imageLibrary
@Binding var chosenImage: UIImage?
@Binding var imagePreview: String?
var body: some View {
VStack(spacing: 0) {
if let metadata = linkPreview {
if let metadata = imagePreview {
ComposeImageView(image: metadata, cancelImage: nil)
} else if let metadata = linkPreview {
ComposeLinkView(linkPreview: metadata, cancelPreview: cancelPreview)
}
if (quotedItem != nil) {
@@ -42,17 +67,30 @@ struct ComposeView: View {
} else if (editingItem != nil) {
ContextItemView(contextItem: $editingItem, editing: $editing, resetMessage: resetMessage)
}
SendMessageView(
sendMessage: { text in
sendMessage(text)
resetLinkPreview()
},
inProgress: inProgress,
message: $message,
keyboardVisible: $keyboardVisible,
editing: $editing
)
.background(.background)
HStack{
Button {
showChooseSource = true
} label: {
Image(systemName: "paperclip")
.resizable()
}
.disabled(editingItem != nil)
.frame(width: 25, height: 25)
.padding(.vertical, 4)
.padding(.leading, 12)
SendMessageView(
sendMessage: { text in
sendMessage(text)
resetLinkPreview()
},
inProgress: inProgress,
message: $message,
keyboardVisible: $keyboardVisible,
editing: $editing
)
.padding(.trailing, 12)
.background(.background)
}
}
.onChange(of: message) { _ in
if message.count > 0 {
@@ -64,6 +102,33 @@ struct ComposeView: View {
.onChange(of: editingItem == nil) { _ in
editing = (editingItem != nil)
}
.confirmationDialog("Add image", isPresented: $showChooseSource, titleVisibility: .visible) {
Button("Take picture") {
imageSource = .camera
showImagePicker = true
}
Button("Choose from library") {
imageSource = .imageLibrary
showImagePicker = true
}
}
.sheet(isPresented: $showImagePicker) {
switch imageSource {
case .imageLibrary:
LibraryImagePicker(image: $chosenImage) {
didSelectItem in showImagePicker = false
}
case .camera:
CameraImagePicker(image: $chosenImage)
}
}
.onChange(of: chosenImage) { image in
if let image = image {
imagePreview = resizeImageToDataSize(cropToSquare(image), maxDataSize: 12500)
} else {
imagePreview = nil
}
}
}
private func showLinkPreview(_ s: String) {
@@ -136,6 +201,8 @@ struct ComposeView_Previews: PreviewProvider {
@State var item: ChatItem? = ChatItem.getSample(1, .directSnd, .now, "hello")
@State var nilItem: ChatItem? = nil
@State var linkPreview: LinkPreview? = nil
@State var chosenImage: UIImage? = nil
@State var imagePreview: String? = nil
return Group {
ComposeView(
@@ -145,7 +212,9 @@ struct ComposeView_Previews: PreviewProvider {
linkPreview: $linkPreview,
sendMessage: { print ($0) },
resetMessage: {},
keyboardVisible: $keyboardVisible
keyboardVisible: $keyboardVisible,
chosenImage: $chosenImage,
imagePreview: $imagePreview
)
ComposeView(
message: $message,
@@ -154,7 +223,9 @@ struct ComposeView_Previews: PreviewProvider {
linkPreview: $linkPreview,
sendMessage: { print ($0) },
resetMessage: {},
keyboardVisible: $keyboardVisible
keyboardVisible: $keyboardVisible,
chosenImage: $chosenImage,
imagePreview: $imagePreview
)
}
}
@@ -62,7 +62,6 @@ struct SendMessageView: View {
.strokeBorder(.secondary, lineWidth: 0.3, antialiased: true)
.frame(height: teHeight)
}
.padding(.horizontal, 12)
.padding(.vertical, 8)
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,37 @@
//
// ComposeImageView.swift
// SimpleX
//
// Created by JRoberts on 11/04/2022.
// Copyright © 2022 SimpleX Chat. All rights reserved.
//
import SwiftUI
struct ComposeImageView: View {
@Environment(\.colorScheme) var colorScheme
let image: String
var cancelImage: (() -> Void)? = nil
var body: some View {
HStack(alignment: .center, spacing: 8) {
if let data = Data(base64Encoded: dropImagePrefix(image)),
let uiImage = UIImage(data: data) {
Image(uiImage: uiImage)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 80, maxHeight: 60)
}
if let cancelImage = cancelImage {
Button { cancelImage() } label: {
Image(systemName: "multiply")
}
}
}
.padding(.vertical, 1)
.padding(.trailing, 12)
.background(colorScheme == .light ? sentColorLight : sentColorDark)
.frame(maxWidth: .infinity)
.padding(.top, 8)
}
}
+1
View File
@@ -69,6 +69,7 @@ struct TerminalView: View {
keyboardVisible: $keyboardVisible,
editing: $editing
)
.padding(.horizontal, 12)
}
}
.navigationViewStyle(.stack)
@@ -113,6 +113,8 @@
649BCD9C280430D100C3A862 /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 649BCD97280430D100C3A862 /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx.a */; };
649BCD9D280430D100C3A862 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 649BCD98280430D100C3A862 /* libgmp.a */; };
649BCD9E280430D100C3A862 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 649BCD99280430D100C3A862 /* libgmpxx.a */; };
649BCDA0280460FD00C3A862 /* ComposeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCD9F280460FD00C3A862 /* ComposeImageView.swift */; };
649BCDA22805D6EF00C3A862 /* ChatItemImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCDA12805D6EF00C3A862 /* ChatItemImageView.swift */; };
64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; };
64AA1C6A27EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; };
64AA1C6C27F3537400AC7277 /* DeletedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */; };
@@ -201,6 +203,8 @@
649BCD97280430D100C3A862 /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx.a"; sourceTree = "<group>"; };
649BCD98280430D100C3A862 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
649BCD99280430D100C3A862 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
649BCD9F280460FD00C3A862 /* ComposeImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeImageView.swift; sourceTree = "<group>"; };
649BCDA12805D6EF00C3A862 /* ChatItemImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemImageView.swift; sourceTree = "<group>"; };
64AA1C6827EE10C800AC7277 /* ContextItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextItemView.swift; sourceTree = "<group>"; };
64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedItemView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -319,6 +323,8 @@
5C5F2B6F27EBC704006A9D5F /* ProfileImage.swift */,
3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */,
3CDBCF4727FF621E00354CDD /* ChatItemLinkView.swift */,
649BCD9F280460FD00C3A862 /* ComposeImageView.swift */,
649BCDA12805D6EF00C3A862 /* ChatItemImageView.swift */,
);
path = Helpers;
sourceTree = "<group>";
@@ -635,6 +641,7 @@
5CC1C99227A6C7F5000D9FF6 /* QRCode.swift in Sources */,
5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */,
5CB9250D27A9432000ACCCDD /* ChatListNavLink.swift in Sources */,
649BCDA0280460FD00C3A862 /* ComposeImageView.swift in Sources */,
5CA059ED279559F40002BEB4 /* ContentView.swift in Sources */,
5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */,
5C3A88CE27DF50170060F1C2 /* DetermineWidth.swift in Sources */,
@@ -649,6 +656,7 @@
5C577F7D27C83AA10006112D /* MarkdownHelp.swift in Sources */,
5CA059EB279559F40002BEB4 /* SimpleXApp.swift in Sources */,
5CCD403727A5F9A200368C90 /* ConnectContactView.swift in Sources */,
649BCDA22805D6EF00C3A862 /* ChatItemImageView.swift in Sources */,
5CCD403A27A5F9BE00368C90 /* CreateGroupView.swift in Sources */,
5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */,
5C764E89279CBCB3000C6508 /* ChatModel.swift in Sources */,