mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
ios wip
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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 */,
|
||||
|
||||
Reference in New Issue
Block a user