发送消息
This commit is contained in:
parent
ed146df357
commit
2431df67df
@ -39,6 +39,7 @@
|
||||
DE9C3A312C0E390200951DEE /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9C3A112C0E390200951DEE /* ContentView.swift */; };
|
||||
DE9C3A322C0E390200951DEE /* copinismApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9C3A122C0E390200951DEE /* copinismApp.swift */; };
|
||||
DE9C3A332C0E390200951DEE /* TestSwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9C3A132C0E390200951DEE /* TestSwiftUIView.swift */; };
|
||||
DE9C3A362C10AA8000951DEE /* MessageBack.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9C3A352C10AA8000951DEE /* MessageBack.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@ -78,6 +79,7 @@
|
||||
DE9C3A122C0E390200951DEE /* copinismApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = copinismApp.swift; sourceTree = "<group>"; };
|
||||
DE9C3A132C0E390200951DEE /* TestSwiftUIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestSwiftUIView.swift; sourceTree = "<group>"; };
|
||||
DE9C3A342C0E420400951DEE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
DE9C3A352C10AA8000951DEE /* MessageBack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBack.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -124,6 +126,7 @@
|
||||
DE9C39E62C0E390200951DEE /* Api.swift */,
|
||||
DE9C39E72C0E390200951DEE /* Req.swift */,
|
||||
DE9C39E82C0E390200951DEE /* Target.swift */,
|
||||
DE9C3A352C10AA8000951DEE /* MessageBack.swift */,
|
||||
);
|
||||
path = api;
|
||||
sourceTree = "<group>";
|
||||
@ -420,6 +423,7 @@
|
||||
DE9C3A292C0E390200951DEE /* Login.swift in Sources */,
|
||||
DE9C3A2E2C0E390200951DEE /* ShoppingView.swift in Sources */,
|
||||
DE9C3A2C2C0E390200951DEE /* TargetItamSwift.swift in Sources */,
|
||||
DE9C3A362C10AA8000951DEE /* MessageBack.swift in Sources */,
|
||||
DE9C3A282C0E390200951DEE /* MessageView.swift in Sources */,
|
||||
DE9C3A1F2C0E390200951DEE /* ArticleCardView.swift in Sources */,
|
||||
DE9C3A182C0E390200951DEE /* TabbarView.swift in Sources */,
|
||||
|
||||
@ -4,6 +4,10 @@
|
||||
<dict>
|
||||
<key>CFBundleAllowMixedLocalizations</key>
|
||||
<true/>
|
||||
<key>CFBundleLocalizations</key>
|
||||
<array>
|
||||
<string>zh_CN</string>
|
||||
</array>
|
||||
<key>IDEPreferLogStreaming</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
|
||||
@ -1,25 +1,26 @@
|
||||
|
||||
|
||||
import SwiftUI
|
||||
import Alamofire
|
||||
|
||||
struct Message: Identifiable {
|
||||
let id: UUID
|
||||
let text: String
|
||||
let sender: String
|
||||
let timestamp: String
|
||||
let isSelf: Bool
|
||||
let avatar: String
|
||||
let num: Int
|
||||
|
||||
init(id: UUID = UUID(), text: String, sender: String, timestamp: String, isSelf: Bool, avatar: String, num: Int = 0) {
|
||||
self.id = id
|
||||
self.text = text
|
||||
self.sender = sender
|
||||
self.timestamp = timestamp
|
||||
self.isSelf = isSelf
|
||||
self.avatar = avatar
|
||||
self.num = num
|
||||
}
|
||||
let text: String
|
||||
let sender: String
|
||||
let timestamp: String
|
||||
let isSelf: Bool
|
||||
let avatar: String
|
||||
let num: Int
|
||||
let image: UIImage?
|
||||
|
||||
init(id: UUID = UUID(), text: String = "", sender: String, timestamp: String, isSelf: Bool, avatar: String, num: Int = 0, image: UIImage? = nil) {
|
||||
self.id = id
|
||||
self.text = text
|
||||
self.sender = sender
|
||||
self.timestamp = timestamp
|
||||
self.isSelf = isSelf
|
||||
self.avatar = avatar
|
||||
self.num = num
|
||||
self.image = image
|
||||
}
|
||||
}
|
||||
|
||||
struct MessageDetailView: View {
|
||||
@ -31,6 +32,7 @@ struct MessageDetailView: View {
|
||||
@State private var selectedFileURL: URL?
|
||||
@State private var isImagePickerPresented = false
|
||||
@State private var isDocumentPickerPresented = false
|
||||
@State private var uploadProgress: Double = 0.0 // 用于跟踪上传进度
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
|
||||
var body: some View {
|
||||
@ -69,11 +71,49 @@ struct MessageDetailView: View {
|
||||
if !message.isSelf {
|
||||
Image("avatar")
|
||||
.CircleImage(size: 30)
|
||||
TextBlackbble(message: message.text)
|
||||
if let image = message.image {
|
||||
ZStack {
|
||||
Image(uiImage: image)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 200, height: 200)
|
||||
.cornerRadius(10)
|
||||
|
||||
if uploadProgress < 1.0 {
|
||||
ProgressView(value: uploadProgress)
|
||||
.progressViewStyle(CircularProgressViewStyle())
|
||||
.scaleEffect(2)
|
||||
.frame(width: 50, height: 50)
|
||||
.background(Color.white.opacity(0.7))
|
||||
.cornerRadius(25)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TextBlackbble(message: message.text)
|
||||
}
|
||||
Spacer()
|
||||
} else {
|
||||
Spacer()
|
||||
TextBubble(message: message.text)
|
||||
if let image = message.image {
|
||||
ZStack {
|
||||
Image(uiImage: image)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 200, height: 200)
|
||||
.cornerRadius(10)
|
||||
|
||||
if uploadProgress < 1.0 {
|
||||
ProgressView(value: uploadProgress)
|
||||
.progressViewStyle(CircularProgressViewStyle())
|
||||
.scaleEffect(2)
|
||||
.frame(width: 50, height: 50)
|
||||
.background(Color.white.opacity(0.7))
|
||||
.cornerRadius(25)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TextBubble(message: message.text)
|
||||
}
|
||||
Image("avatar")
|
||||
.CircleImage(size: 30)
|
||||
}
|
||||
@ -88,7 +128,7 @@ struct MessageDetailView: View {
|
||||
|
||||
HStack {
|
||||
Button(action: {
|
||||
isDocumentPickerPresented = true
|
||||
isImagePickerPresented = true
|
||||
}) {
|
||||
Image(systemName: "paperclip")
|
||||
.font(.system(size: 18))
|
||||
@ -120,7 +160,10 @@ struct MessageDetailView: View {
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.bottom, 8)
|
||||
.sheet(isPresented: $isImagePickerPresented) {
|
||||
ImagePicker(selectedImage: $selectedImage)
|
||||
ImagePicker(selectedImage: $selectedImage, onImagePicked: { image in
|
||||
print(image)
|
||||
sendImageMessage(image: image)
|
||||
})
|
||||
}
|
||||
.sheet(isPresented: $isDocumentPickerPresented) {
|
||||
DocumentPicker(selectedFileURL: $selectedFileURL)
|
||||
@ -135,9 +178,38 @@ struct MessageDetailView: View {
|
||||
private func sendMessage() {
|
||||
let newMessage = Message(text: replyText, sender: "你", timestamp: currentTimestamp(), isSelf: true, avatar: "avatar")
|
||||
messages.append(newMessage)
|
||||
// let messageData = ["text": replyText, "sender": "你", "timestamp": currentTimestamp(), "isSelf": true, "avatar": "avatar"]
|
||||
// makeAlamofireRequest(api: "YOUR_API_ENDPOINT_HERE", method: .post, param: messageData) { (result: Result<HTTPBinResponse<Message>, AFError>) in
|
||||
// switch result {
|
||||
// case .success(let response):
|
||||
// print("Message sent successfully: \(response)")
|
||||
// case .failure(let error):
|
||||
// print("Error sending message: \(error)")
|
||||
// }
|
||||
// }
|
||||
|
||||
replyText = ""
|
||||
}
|
||||
|
||||
private func sendImageMessage(image: UIImage) {
|
||||
let newMessage = Message(sender: "你", timestamp: currentTimestamp(), isSelf: true, avatar: "avatar", image: image)
|
||||
messages.append(newMessage)
|
||||
|
||||
// 将进度重置为 0.0
|
||||
uploadProgress = 0.0
|
||||
|
||||
// 假设你想以 base64 字符串的形式发送图片
|
||||
if let imageData = image.jpegData(compressionQuality: 0.8) {
|
||||
uploadFile(api: apiprefixV1+upload, file: imageData) { progress in
|
||||
// 更新进度
|
||||
self.uploadProgress = progress
|
||||
} completion: { (result: Result<HTTPBinResponse<Token>, AFError>) in
|
||||
print(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func currentTimestamp() -> String {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "h:mm a"
|
||||
@ -177,23 +249,21 @@ struct TextBubble: View {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#Preview {
|
||||
MessageDetailView()
|
||||
}
|
||||
|
||||
// ImagePicker implementation
|
||||
struct ImagePicker: UIViewControllerRepresentable {
|
||||
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
|
||||
let parent: ImagePicker
|
||||
let onImagePicked: (UIImage) -> Void
|
||||
|
||||
init(parent: ImagePicker) {
|
||||
init(parent: ImagePicker, onImagePicked: @escaping (UIImage) -> Void) {
|
||||
self.parent = parent
|
||||
self.onImagePicked = onImagePicked
|
||||
}
|
||||
|
||||
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
|
||||
if let image = info[.originalImage] as? UIImage {
|
||||
parent.selectedImage = image
|
||||
onImagePicked(image)
|
||||
}
|
||||
picker.dismiss(animated: true)
|
||||
}
|
||||
@ -204,9 +274,10 @@ struct ImagePicker: UIViewControllerRepresentable {
|
||||
}
|
||||
|
||||
@Binding var selectedImage: UIImage?
|
||||
let onImagePicked: (UIImage) -> Void
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(parent: self)
|
||||
Coordinator(parent: self, onImagePicked: onImagePicked)
|
||||
}
|
||||
|
||||
func makeUIViewController(context: Context) -> UIImagePickerController {
|
||||
@ -220,6 +291,8 @@ struct ImagePicker: UIViewControllerRepresentable {
|
||||
|
||||
// DocumentPicker implementation
|
||||
struct DocumentPicker: UIViewControllerRepresentable {
|
||||
@Binding var selectedFileURL: URL?
|
||||
|
||||
class Coordinator: NSObject, UIDocumentPickerDelegate {
|
||||
let parent: DocumentPicker
|
||||
|
||||
@ -229,25 +302,27 @@ struct DocumentPicker: UIViewControllerRepresentable {
|
||||
|
||||
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
||||
parent.selectedFileURL = urls.first
|
||||
controller.dismiss(animated: true)
|
||||
}
|
||||
|
||||
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
|
||||
controller.dismiss(animated: true)
|
||||
parent.selectedFileURL = nil
|
||||
}
|
||||
}
|
||||
|
||||
@Binding var selectedFileURL: URL?
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(parent: self)
|
||||
}
|
||||
|
||||
func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
|
||||
let picker = UIDocumentPickerViewController(forOpeningContentTypes: [.item])
|
||||
picker.delegate = context.coordinator
|
||||
return picker
|
||||
let controller = UIDocumentPickerViewController(forOpeningContentTypes: [.item])
|
||||
controller.delegate = context.coordinator
|
||||
return controller
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: Context) {}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
MessageDetailView()
|
||||
}
|
||||
|
||||
|
||||
@ -11,13 +11,15 @@ import Foundation
|
||||
|
||||
let local = "http://127.0.0.1:9999"
|
||||
|
||||
let dev = "http://192.168.0.125:9999"
|
||||
let dev = "http://192.168.1.5:9999"
|
||||
|
||||
let dev1 = "http://192.168.0.121:9999"
|
||||
|
||||
let pp = "http://43.136.169.205:9999"
|
||||
|
||||
let release = "https://api.flyaha.top"
|
||||
|
||||
let apiprefixV1 = release+"/api/v1"
|
||||
let apiprefixV1 = local+"/api/v1"
|
||||
|
||||
|
||||
var loginApi = "/user/login"
|
||||
@ -31,3 +33,5 @@ var targetApi = "/target/list"
|
||||
|
||||
var targeMetApi = "/target/me/list"
|
||||
|
||||
var upload = "/todo/message"
|
||||
|
||||
|
||||
8
copinism/api/MessageBack.swift
Normal file
8
copinism/api/MessageBack.swift
Normal file
@ -0,0 +1,8 @@
|
||||
//
|
||||
// MessageBack.swift
|
||||
// copinism
|
||||
//
|
||||
// Created by 黄仕杰 on 2024/6/5.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@ -33,3 +33,22 @@ func makeAlamofireRequest<T: Decodable,Parame: Encodable>(api: String,
|
||||
}
|
||||
|
||||
|
||||
func uploadFile<T: Decodable>(api: String,file: Data,progressHandler: @escaping (Double) -> Void,completion: @escaping (Result<HTTPBinResponse<T>, AFError>) -> Void) {
|
||||
var headers: HTTPHeaders = [
|
||||
"Accept": "application/json",
|
||||
"Content-Type":"application/octet-stream"
|
||||
|
||||
]
|
||||
let loginStatus = checkLoginStatus()
|
||||
if loginStatus.login {
|
||||
headers.add(name: "Authorization", value: loginStatus.token)
|
||||
}
|
||||
AF.upload(file, to: api,headers: headers)
|
||||
.uploadProgress { progress in
|
||||
progressHandler(progress.fractionCompleted)
|
||||
}
|
||||
.validate().responseDecodable(of: HTTPBinResponse<T>.self) { response in
|
||||
completion(response.result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BIN
【凡人修仙传】那些无法超越的台词和画面!.mp4
Normal file
BIN
【凡人修仙传】那些无法超越的台词和画面!.mp4
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user