mv-experiment #1
|
@ -49,17 +49,17 @@ struct Message: DBStorable, Equatable {
|
||||||
static let databaseTableName = "messages"
|
static let databaseTableName = "messages"
|
||||||
|
|
||||||
let id: String
|
let id: String
|
||||||
let type: MessageType
|
var type: MessageType
|
||||||
let date: Date
|
let date: Date
|
||||||
var contentType: MessageContentType
|
var contentType: MessageContentType
|
||||||
var status: MessageStatus
|
var status: MessageStatus
|
||||||
|
|
||||||
let from: String
|
var from: String
|
||||||
let to: String?
|
var to: String?
|
||||||
|
|
||||||
var body: String?
|
var body: String?
|
||||||
let subject: String?
|
var subject: String?
|
||||||
let thread: String?
|
var thread: String?
|
||||||
var oobUrl: String?
|
var oobUrl: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,4 +77,20 @@ extension Message {
|
||||||
try updatedMessage.update(db, columns: ["status"])
|
try updatedMessage.update(db, columns: ["status"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static var blank: Message {
|
||||||
|
Message(
|
||||||
|
id: UUID().uuidString,
|
||||||
|
type: .chat,
|
||||||
|
date: Date(),
|
||||||
|
contentType: .text,
|
||||||
|
status: .pending,
|
||||||
|
from: "",
|
||||||
|
to: nil,
|
||||||
|
body: nil,
|
||||||
|
subject: nil,
|
||||||
|
thread: nil,
|
||||||
|
oobUrl: nil
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,6 @@ final class ConversationStore: ObservableObject {
|
||||||
|
|
||||||
private(set) var roster: Roster
|
private(set) var roster: Roster
|
||||||
private let client: Client
|
private let client: Client
|
||||||
private let blockSize = Const.messagesPageSize
|
|
||||||
private let messagesMax = Const.messagesMaxSize
|
|
||||||
|
|
||||||
private var messagesCancellable: AnyCancellable?
|
private var messagesCancellable: AnyCancellable?
|
||||||
|
|
||||||
|
@ -25,28 +23,18 @@ final class ConversationStore: ObservableObject {
|
||||||
|
|
||||||
extension ConversationStore {
|
extension ConversationStore {
|
||||||
func sendMessage(_ message: String) async {
|
func sendMessage(_ message: String) async {
|
||||||
// prepare message
|
var msg = Message.blank
|
||||||
let message = Message(
|
msg.from = roster.bareJid
|
||||||
id: UUID().uuidString,
|
msg.to = roster.contactBareJid
|
||||||
type: .chat,
|
msg.body = message
|
||||||
date: Date(),
|
|
||||||
contentType: .text,
|
|
||||||
status: .pending,
|
|
||||||
from: roster.bareJid,
|
|
||||||
to: roster.contactBareJid,
|
|
||||||
body: message,
|
|
||||||
subject: nil,
|
|
||||||
thread: nil,
|
|
||||||
oobUrl: nil
|
|
||||||
)
|
|
||||||
|
|
||||||
// store as pending on db, and send
|
// store as pending on db, and send
|
||||||
do {
|
do {
|
||||||
try await message.save()
|
try await msg.save()
|
||||||
try await client.sendMessage(message)
|
try await client.sendMessage(msg)
|
||||||
try await message.setStatus(.sent)
|
try await msg.setStatus(.sent)
|
||||||
} catch {
|
} catch {
|
||||||
try? await message.setStatus(.error)
|
try? await msg.setStatus(.error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +55,10 @@ extension ConversationStore {
|
||||||
|
|
||||||
func sendCaptured(_ data: Data, _ type: GalleryMediaType) async {
|
func sendCaptured(_ data: Data, _ type: GalleryMediaType) async {
|
||||||
// save locally and make message
|
// save locally and make message
|
||||||
let messageId = UUID().uuidString
|
var message = Message.blank
|
||||||
|
message.from = roster.bareJid
|
||||||
|
message.to = roster.contactBareJid
|
||||||
|
|
||||||
let localName: String
|
let localName: String
|
||||||
let msgType: AttachmentType
|
let msgType: AttachmentType
|
||||||
do {
|
do {
|
||||||
|
@ -78,11 +69,11 @@ extension ConversationStore {
|
||||||
let msgType: AttachmentType
|
let msgType: AttachmentType
|
||||||
switch type {
|
switch type {
|
||||||
case .photo:
|
case .photo:
|
||||||
localName = "\(messageId)_\(fileId).jpg"
|
localName = "\(message.id)_\(fileId).jpg"
|
||||||
msgType = .image
|
msgType = .image
|
||||||
|
|
||||||
case .video:
|
case .video:
|
||||||
localName = "\(messageId)_\(fileId).mov"
|
localName = "\(message.id)_\(fileId).mov"
|
||||||
msgType = .video
|
msgType = .video
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,25 +88,13 @@ extension ConversationStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// save message
|
// save message
|
||||||
let message = Message(
|
message.contentType = .attachment(
|
||||||
id: UUID().uuidString,
|
Attachment(
|
||||||
type: .chat,
|
type: msgType,
|
||||||
date: Date(),
|
localName: localName,
|
||||||
contentType: .attachment(
|
thumbnailName: nil,
|
||||||
Attachment(
|
remotePath: nil
|
||||||
type: msgType,
|
)
|
||||||
localName: localName,
|
|
||||||
thumbnailName: nil,
|
|
||||||
remotePath: nil
|
|
||||||
)
|
|
||||||
),
|
|
||||||
status: .pending,
|
|
||||||
from: roster.bareJid,
|
|
||||||
to: roster.contactBareJid,
|
|
||||||
body: nil,
|
|
||||||
subject: nil,
|
|
||||||
thread: nil,
|
|
||||||
oobUrl: nil
|
|
||||||
)
|
)
|
||||||
do {
|
do {
|
||||||
try await message.save()
|
try await message.save()
|
||||||
|
|
|
@ -2,15 +2,6 @@ import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
enum Const {
|
enum Const {
|
||||||
// // Network
|
|
||||||
// #if DEBUG
|
|
||||||
// static let baseUrl = "staging.some.com/api"
|
|
||||||
// #else
|
|
||||||
// static let baseUrl = "prod.some.com/api"
|
|
||||||
// #endif
|
|
||||||
// static let requestTimeout = 15.0
|
|
||||||
// static let networkLogging = true
|
|
||||||
|
|
||||||
// App
|
// App
|
||||||
static var appVersion: String {
|
static var appVersion: String {
|
||||||
let info = Bundle.main.infoDictionary
|
let info = Bundle.main.infoDictionary
|
||||||
|
@ -54,8 +45,4 @@ enum Const {
|
||||||
|
|
||||||
// Lenght in days for MAM request
|
// Lenght in days for MAM request
|
||||||
static let mamRequestDaysLength = 30
|
static let mamRequestDaysLength = 30
|
||||||
|
|
||||||
// Limits for messages pagination
|
|
||||||
static let messagesPageSize = 20 // size for block requesting
|
|
||||||
static let messagesMaxSize = 100 // total messages in memory
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,8 @@ struct ConversationMessageContainer: View {
|
||||||
EmbededMapView(location: msgText.getLatLon)
|
EmbededMapView(location: msgText.getLatLon)
|
||||||
} else if let msgText = message.body, msgText.isContact {
|
} else if let msgText = message.body, msgText.isContact {
|
||||||
ContactView(message: message)
|
ContactView(message: message)
|
||||||
// } else if message.attachmentType != nil {
|
} else if case .attachment(let attachment) = message.contentType {
|
||||||
// AttachmentView(message: message)
|
AttachmentView(message: message, attachment: attachment)
|
||||||
} else {
|
} else {
|
||||||
Text(message.body ?? "...")
|
Text(message.body ?? "...")
|
||||||
.font(.body2)
|
.font(.body2)
|
||||||
|
@ -98,107 +98,108 @@ private struct ContactView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// private struct AttachmentView: View {
|
private struct AttachmentView: View {
|
||||||
// let message: Message
|
let message: Message
|
||||||
//
|
let attachment: Attachment
|
||||||
// var body: some View {
|
|
||||||
// if message.attachmentDownloadFailed || (message.attachmentLocalName != nil && message.sentError) {
|
var body: some View {
|
||||||
// failed
|
if message.status == .error {
|
||||||
// } else {
|
failed
|
||||||
// switch message.attachmentType {
|
} else {
|
||||||
// case .image:
|
switch attachment.type {
|
||||||
// if let thumbnail = thumbnail() {
|
case .image:
|
||||||
// thumbnail
|
if let thumbnail = thumbnail() {
|
||||||
// .resizable()
|
thumbnail
|
||||||
// .aspectRatio(contentMode: .fit)
|
.resizable()
|
||||||
// .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
|
.aspectRatio(contentMode: .fit)
|
||||||
// } else {
|
.frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
|
||||||
// placeholder
|
} else {
|
||||||
// }
|
placeholder
|
||||||
//
|
}
|
||||||
// case .movie:
|
|
||||||
// if let file = message.attachmentLocalPath {
|
case .video:
|
||||||
// VideoPlayerView(url: file)
|
if let file = attachment.localPath {
|
||||||
// .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
|
VideoPlayerView(url: file)
|
||||||
// } else {
|
.frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
|
||||||
// placeholder
|
} else {
|
||||||
// }
|
placeholder
|
||||||
//
|
}
|
||||||
// case .file:
|
|
||||||
// if let file = message.attachmentLocalPath {
|
case .file:
|
||||||
// DocumentPreview(url: file)
|
if let file = attachment.localPath {
|
||||||
// .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
|
DocumentPreview(url: file)
|
||||||
// } else {
|
.frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
|
||||||
// placeholder
|
} else {
|
||||||
// }
|
placeholder
|
||||||
//
|
}
|
||||||
// default:
|
|
||||||
// placeholder
|
default:
|
||||||
// }
|
placeholder
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//
|
}
|
||||||
// @ViewBuilder private var placeholder: some View {
|
|
||||||
// Rectangle()
|
@ViewBuilder private var placeholder: some View {
|
||||||
// .foregroundColor(.Material.Background.dark)
|
Rectangle()
|
||||||
// .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
|
.foregroundColor(.Material.Background.dark)
|
||||||
// .overlay {
|
.frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
|
||||||
// ZStack {
|
.overlay {
|
||||||
// ProgressView()
|
ZStack {
|
||||||
// .scaleEffect(1.5)
|
ProgressView()
|
||||||
// .progressViewStyle(CircularProgressViewStyle(tint: .Material.Elements.active))
|
.scaleEffect(1.5)
|
||||||
// let imageName = progressImageName(message.attachmentType ?? .file)
|
.progressViewStyle(CircularProgressViewStyle(tint: .Material.Elements.active))
|
||||||
// Image(systemName: imageName)
|
let imageName = progressImageName(attachment.type ?? .file)
|
||||||
// .font(.body1)
|
Image(systemName: imageName)
|
||||||
// .foregroundColor(.Material.Elements.active)
|
.font(.body1)
|
||||||
// }
|
.foregroundColor(.Material.Elements.active)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//
|
}
|
||||||
// @ViewBuilder private var failed: some View {
|
|
||||||
// Rectangle()
|
@ViewBuilder private var failed: some View {
|
||||||
// .foregroundColor(.Material.Background.dark)
|
Rectangle()
|
||||||
// .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
|
.foregroundColor(.Material.Background.dark)
|
||||||
// .overlay {
|
.frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
|
||||||
// ZStack {
|
.overlay {
|
||||||
// VStack {
|
ZStack {
|
||||||
// Text(L10n.Attachment.Downloading.retry)
|
VStack {
|
||||||
// .font(.body3)
|
Text(L10n.Attachment.Downloading.retry)
|
||||||
// .foregroundColor(.Rainbow.red500)
|
.font(.body3)
|
||||||
// Image(systemName: "exclamationmark.arrow.triangle.2.circlepath")
|
.foregroundColor(.Rainbow.red500)
|
||||||
// .font(.body1)
|
Image(systemName: "exclamationmark.arrow.triangle.2.circlepath")
|
||||||
// .foregroundColor(.Rainbow.red500)
|
.font(.body1)
|
||||||
// }
|
.foregroundColor(.Rainbow.red500)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// .onTapGesture {
|
}
|
||||||
// if let url = message.attachmentRemotePath {
|
.onTapGesture {
|
||||||
// store.dispatch(.fileAction(.downloadAttachmentFile(messageId: message.id, attachmentRemotePath: url)))
|
// if let url = message.attachmentRemotePath {
|
||||||
// } else if message.attachmentLocalName != nil && message.sentError {
|
// store.dispatch(.fileAction(.downloadAttachmentFile(messageId: message.id, attachmentRemotePath: url)))
|
||||||
// store.dispatch(.sharingAction(.retrySharing(messageId: message.id)))
|
// } else if message.attachmentLocalName != nil && message.sentError {
|
||||||
// }
|
// store.dispatch(.sharingAction(.retrySharing(messageId: message.id)))
|
||||||
// }
|
// }
|
||||||
// }
|
}
|
||||||
//
|
}
|
||||||
// private func progressImageName(_ type: MessageAttachmentType) -> String {
|
|
||||||
// switch type {
|
private func progressImageName(_ type: AttachmentType) -> String {
|
||||||
// case .image:
|
switch type {
|
||||||
// return "photo"
|
case .image:
|
||||||
// case .audio:
|
return "photo"
|
||||||
// return "music.note"
|
case .audio:
|
||||||
// case .movie:
|
return "music.note"
|
||||||
// return "film"
|
case .video:
|
||||||
// case .file:
|
return "film"
|
||||||
// return "doc"
|
case .file:
|
||||||
// }
|
return "doc"
|
||||||
// }
|
}
|
||||||
//
|
}
|
||||||
// private func thumbnail() -> Image? {
|
|
||||||
// guard let thumbnailPath = message.attachmentThumbnailPath else { return nil }
|
private func thumbnail() -> Image? {
|
||||||
// guard let uiImage = UIImage(contentsOfFile: thumbnailPath.path()) else { return nil }
|
guard let thumbnailPath = attachment.thumbnailPath else { return nil }
|
||||||
// return Image(uiImage: uiImage)
|
guard let uiImage = UIImage(contentsOfFile: thumbnailPath.path()) else { return nil }
|
||||||
// }
|
return Image(uiImage: uiImage)
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Make video player better!
|
// TODO: Make video player better!
|
||||||
private struct VideoPlayerView: UIViewControllerRepresentable {
|
private struct VideoPlayerView: UIViewControllerRepresentable {
|
||||||
|
|
|
@ -50,7 +50,7 @@ struct ConversationMessageRow: View {
|
||||||
}
|
}
|
||||||
if value.translation.width <= targetWidth {
|
if value.translation.width <= targetWidth {
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.02) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.02) {
|
||||||
// store.dispatch(.conversationAction(.setReplyText(message.body ?? "")))
|
conversation.replyText = message.body ?? ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue