596 lines
24 KiB
Swift
Executable File
596 lines
24 KiB
Swift
Executable File
import PhotosUI
|
|
import SwiftUI
|
|
|
|
// MARK: - Inbox / History
|
|
|
|
struct InboxTabView: View {
|
|
@EnvironmentObject private var services: AppServices
|
|
private var repo: ChatRepository { services.repository }
|
|
private var chat: ChatState { repo.state }
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
LazyVStack(alignment: .leading, spacing: 10) {
|
|
Text(L10n.tr("tab_inbox"))
|
|
.font(.title2.weight(.bold))
|
|
.padding(.horizontal, 16)
|
|
if chat.inboxResults.isEmpty {
|
|
Text(L10n.tr("inbox_empty"))
|
|
.foregroundStyle(YpChatTheme.textMuted)
|
|
.padding(.horizontal, 16)
|
|
}
|
|
ForEach(chat.inboxResults, id: \.userName) { item in
|
|
Button {
|
|
repo.openConversation(userName: item.userName)
|
|
} label: {
|
|
HStack {
|
|
Text(item.userName).fontWeight(.semibold)
|
|
Spacer()
|
|
Text(L10n.inboxNew(item.unreadCount))
|
|
.font(.subheadline)
|
|
.foregroundStyle(YpChatTheme.textMuted)
|
|
}
|
|
.padding(16)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.background(YpChatTheme.surface, in: RoundedRectangle(cornerRadius: 12))
|
|
}
|
|
.buttonStyle(.plain)
|
|
.padding(.horizontal, 16)
|
|
}
|
|
}
|
|
.padding(.vertical, 16)
|
|
}
|
|
}
|
|
}
|
|
|
|
struct HistoryTabView: View {
|
|
@EnvironmentObject private var services: AppServices
|
|
private var repo: ChatRepository { services.repository }
|
|
private var chat: ChatState { repo.state }
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
LazyVStack(alignment: .leading, spacing: 10) {
|
|
Text(L10n.tr("tab_history"))
|
|
.font(.title2.weight(.bold))
|
|
.padding(.horizontal, 16)
|
|
if chat.historyResults.isEmpty {
|
|
Text(L10n.tr("history_empty"))
|
|
.foregroundStyle(YpChatTheme.textMuted)
|
|
.padding(.horizontal, 16)
|
|
}
|
|
ForEach(chat.historyResults, id: \.userName) { item in
|
|
Button {
|
|
repo.openConversation(userName: item.userName)
|
|
} label: {
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text(item.userName).fontWeight(.semibold)
|
|
Text(item.lastMessage?.message ?? L10n.tr("no_message"))
|
|
.font(.subheadline)
|
|
.foregroundStyle(YpChatTheme.textMuted)
|
|
}
|
|
.padding(16)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.background(YpChatTheme.surface, in: RoundedRectangle(cornerRadius: 12))
|
|
}
|
|
.buttonStyle(.plain)
|
|
.padding(.horizontal, 16)
|
|
}
|
|
}
|
|
.padding(.vertical, 16)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Console
|
|
|
|
struct ConsoleTabView: View {
|
|
@EnvironmentObject private var services: AppServices
|
|
private var repo: ChatRepository { services.repository }
|
|
private var chat: ChatState { repo.state }
|
|
|
|
@State private var input = ""
|
|
|
|
private var placeholder: String {
|
|
if chat.awaitingLoginUsername { return L10n.tr("feedback_admin_user") }
|
|
if chat.awaitingLoginPassword { return L10n.tr("feedback_admin_password") }
|
|
return L10n.tr("console_placeholder")
|
|
}
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
Text(L10n.tr("console_title"))
|
|
.font(.title2.weight(.bold))
|
|
|
|
TextField(placeholder, text: $input)
|
|
.textFieldStyle(.roundedBorder)
|
|
|
|
Button(L10n.tr("console_send")) {
|
|
repo.sendMessage(text: input)
|
|
input = ""
|
|
}
|
|
.buttonStyle(.borderedProminent)
|
|
.tint(YpChatTheme.primary600)
|
|
.disabled(input.isEmpty)
|
|
|
|
if chat.commandLines.isEmpty, chat.commandTable == nil {
|
|
Text(L10n.tr("console_empty"))
|
|
.foregroundStyle(YpChatTheme.textMuted)
|
|
}
|
|
|
|
ForEach(Array(chat.commandLines.enumerated()), id: \.offset) { _, line in
|
|
consoleLineCard(line: localizeRuntimeMessage(line), kind: chat.commandKind)
|
|
}
|
|
|
|
if let table = chat.commandTable {
|
|
commandTableCard(table)
|
|
}
|
|
}
|
|
.padding(16)
|
|
}
|
|
}
|
|
|
|
private func consoleLineCard(line: String, kind: String?) -> some View {
|
|
let k = kind ?? ""
|
|
let bg: Color = {
|
|
if k.hasPrefix("login") { return YpChatTheme.surfaceSoftBlue }
|
|
if k == "info" { return YpChatTheme.surfaceSoftGreen }
|
|
if k == "error" { return YpChatTheme.surfaceSoftRed }
|
|
return YpChatTheme.surfaceSubtle
|
|
}()
|
|
let border: Color = {
|
|
if k.hasPrefix("login") { return Color(red: 0.79, green: 0.86, blue: 0.93) }
|
|
if k == "info" { return Color(red: 0.81, green: 0.89, blue: 0.83) }
|
|
if k == "error" { return Color(red: 0.94, green: 0.79, blue: 0.79) }
|
|
return YpChatTheme.border
|
|
}()
|
|
let fg = k == "error" ? YpChatTheme.danger : YpChatTheme.textStrong
|
|
return Text(line)
|
|
.font(.body)
|
|
.foregroundStyle(fg)
|
|
.padding(14)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.background(bg, in: RoundedRectangle(cornerRadius: 12))
|
|
.overlay(RoundedRectangle(cornerRadius: 12).stroke(border, lineWidth: 1))
|
|
}
|
|
|
|
private func commandTableCard(_ table: CommandTableState) -> some View {
|
|
VStack(alignment: .leading, spacing: 10) {
|
|
Text(table.title).fontWeight(.bold).foregroundStyle(YpChatTheme.textStrong)
|
|
if !table.columns.isEmpty {
|
|
HStack {
|
|
ForEach(table.columns, id: \.self) { col in
|
|
Text(col).fontWeight(.bold).foregroundStyle(YpChatTheme.primary700)
|
|
Spacer(minLength: 8)
|
|
}
|
|
}
|
|
.padding(.horizontal, 10)
|
|
.padding(.vertical, 8)
|
|
.background(YpChatTheme.primary100, in: RoundedRectangle(cornerRadius: 12))
|
|
}
|
|
ForEach(Array(table.rows.enumerated()), id: \.offset) { _, row in
|
|
HStack {
|
|
ForEach(Array(row.enumerated()), id: \.offset) { _, cell in
|
|
Text(cell).foregroundStyle(YpChatTheme.textMuted)
|
|
Spacer(minLength: 8)
|
|
}
|
|
}
|
|
.padding(.horizontal, 10)
|
|
.padding(.vertical, 8)
|
|
.background(YpChatTheme.surfaceSubtle, in: RoundedRectangle(cornerRadius: 12))
|
|
}
|
|
}
|
|
.padding(16)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.background(YpChatTheme.surface, in: RoundedRectangle(cornerRadius: 12))
|
|
.overlay(RoundedRectangle(cornerRadius: 12).stroke(YpChatTheme.border, lineWidth: 1))
|
|
}
|
|
}
|
|
|
|
// MARK: - More
|
|
|
|
struct MoreTabView: View {
|
|
@Binding var section: MoreSection
|
|
|
|
var body: some View {
|
|
Group {
|
|
switch section {
|
|
case .overview:
|
|
moreOverview
|
|
case .feedback:
|
|
FeedbackDetailView { section = .overview }
|
|
case .partners:
|
|
PartnersDetailView { section = .overview }
|
|
case .faq:
|
|
staticContent(title: L10n.tr("faq_title"), body: L10n.tr("faq_body")) { section = .overview }
|
|
case .rules:
|
|
staticContent(title: L10n.tr("rules_title"), body: L10n.tr("rules_body")) { section = .overview }
|
|
case .safety:
|
|
staticContent(title: L10n.tr("safety_title"), body: L10n.tr("safety_body")) { section = .overview }
|
|
case .imprint:
|
|
staticContent(title: L10n.tr("imprint_title"), body: L10n.tr("imprint_body")) { section = .overview }
|
|
}
|
|
}
|
|
}
|
|
|
|
private var moreOverview: some View {
|
|
ScrollView {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
Text(L10n.tr("more_title"))
|
|
.font(.title2.weight(.bold))
|
|
moreLink(L10n.tr("more_feedback"), L10n.tr("feedback_comment")) { section = .feedback }
|
|
moreLink(L10n.tr("more_partners"), L10n.tr("partners_intro")) { section = .partners }
|
|
moreLink(L10n.tr("more_faq"), L10n.tr("faq_intro")) { section = .faq }
|
|
moreLink(L10n.tr("more_rules"), L10n.tr("rules_intro")) { section = .rules }
|
|
moreLink(L10n.tr("more_safety"), L10n.tr("safety_intro")) { section = .safety }
|
|
moreLink(L10n.tr("more_imprint"), L10n.tr("imprint_intro")) { section = .imprint }
|
|
}
|
|
.padding(16)
|
|
}
|
|
}
|
|
|
|
private func moreLink(_ title: String, _ subtitle: String, action: @escaping () -> Void) -> some View {
|
|
Button(action: action) {
|
|
VStack(alignment: .leading, spacing: 6) {
|
|
Text(title).fontWeight(.semibold).foregroundStyle(YpChatTheme.textStrong)
|
|
Text(subtitle).font(.subheadline).foregroundStyle(YpChatTheme.textMuted)
|
|
}
|
|
.padding(16)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.background(YpChatTheme.surface, in: RoundedRectangle(cornerRadius: 12))
|
|
}
|
|
.buttonStyle(.plain)
|
|
}
|
|
|
|
private func staticContent(title: String, body: String, onBack: @escaping () -> Void) -> some View {
|
|
ScrollView {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
HStack {
|
|
Button(L10n.tr("more_back"), action: onBack)
|
|
.foregroundStyle(YpChatTheme.primary700)
|
|
Text(title)
|
|
.font(.title2.weight(.bold))
|
|
}
|
|
Text(body).foregroundStyle(YpChatTheme.textStrong)
|
|
}
|
|
.padding(16)
|
|
}
|
|
}
|
|
}
|
|
|
|
struct FeedbackDetailView: View {
|
|
@EnvironmentObject private var services: AppServices
|
|
var onBack: () -> Void
|
|
|
|
private var repo: ChatRepository { services.repository }
|
|
private var chat: ChatState { repo.state }
|
|
|
|
@State private var comment = ""
|
|
@State private var adminUser = ""
|
|
@State private var adminPassword = ""
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
HStack {
|
|
Button(L10n.tr("more_back"), action: onBack)
|
|
.foregroundStyle(YpChatTheme.primary700)
|
|
Text(L10n.tr("feedback_title"))
|
|
.font(.title2.weight(.bold))
|
|
}
|
|
|
|
TextField(L10n.tr("feedback_comment"), text: $comment, axis: .vertical)
|
|
.lineLimit(4 ... 8)
|
|
.textFieldStyle(.roundedBorder)
|
|
|
|
Button(L10n.tr("feedback_send")) {
|
|
Task {
|
|
await repo.submitFeedback(comment: comment)
|
|
comment = ""
|
|
}
|
|
}
|
|
.buttonStyle(.borderedProminent)
|
|
.tint(YpChatTheme.primary600)
|
|
.disabled(comment.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
|
|
|
|
if let fm = chat.feedbackMessage {
|
|
Text(localizeRuntimeMessage(fm))
|
|
.font(.footnote)
|
|
.foregroundStyle(YpChatTheme.textMuted)
|
|
}
|
|
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
if chat.feedbackAdminAuthenticated {
|
|
Text("\(L10n.tr("feedback_admin_user")): \(chat.feedbackAdminUserName ?? "")")
|
|
.fontWeight(.semibold)
|
|
Button(L10n.tr("feedback_admin_logout")) {
|
|
Task { await repo.logoutFeedbackAdmin() }
|
|
}
|
|
.buttonStyle(.bordered)
|
|
.tint(YpChatTheme.primary500)
|
|
} else {
|
|
TextField(L10n.tr("feedback_admin_user"), text: $adminUser)
|
|
.textFieldStyle(.roundedBorder)
|
|
SecureField(L10n.tr("feedback_admin_password"), text: $adminPassword)
|
|
.textFieldStyle(.roundedBorder)
|
|
Button(L10n.tr("feedback_admin_login")) {
|
|
Task {
|
|
await repo.loginFeedbackAdmin(username: adminUser, password: adminPassword)
|
|
}
|
|
}
|
|
.buttonStyle(.bordered)
|
|
.tint(YpChatTheme.primary500)
|
|
}
|
|
if let err = chat.feedbackAdminError {
|
|
Text(localizeRuntimeMessage(err))
|
|
.foregroundStyle(YpChatTheme.danger)
|
|
.font(.footnote)
|
|
}
|
|
}
|
|
.padding(16)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.background(YpChatTheme.surfaceSubtle, in: RoundedRectangle(cornerRadius: 12))
|
|
|
|
if chat.feedbackItems.isEmpty {
|
|
Text(L10n.tr("feedback_empty"))
|
|
.foregroundStyle(YpChatTheme.textMuted)
|
|
}
|
|
ForEach(Array(chat.feedbackItems.enumerated()), id: \.offset) { _, item in
|
|
feedbackRow(item)
|
|
}
|
|
}
|
|
.padding(16)
|
|
}
|
|
}
|
|
|
|
private func feedbackRow(_ item: FeedbackItemDto) -> some View {
|
|
VStack(alignment: .leading, spacing: 6) {
|
|
HStack {
|
|
Text(item.name.flatMap { $0.isEmpty ? nil : $0 } ?? L10n.tr("anonymous"))
|
|
.fontWeight(.semibold)
|
|
Spacer()
|
|
if chat.feedbackAdminAuthenticated {
|
|
Button(L10n.tr("feedback_delete"), role: .destructive) {
|
|
Task { await repo.deleteFeedback(id: item.id) }
|
|
}
|
|
.font(.caption)
|
|
}
|
|
}
|
|
Text(item.comment).foregroundStyle(YpChatTheme.textStrong)
|
|
let meta = [item.country, item.gender, item.age.map { String($0) }]
|
|
.compactMap { $0 }
|
|
.filter { !$0.isEmpty }
|
|
if !meta.isEmpty {
|
|
Text(meta.joined(separator: L10n.tr("feedback_meta_separator")))
|
|
.font(.caption)
|
|
.foregroundStyle(YpChatTheme.textMuted)
|
|
}
|
|
if let ts = formatFeedbackTimestamp(item.createdAt) {
|
|
Text(L10n.feedbackCreatedAt(ts))
|
|
.font(.caption2)
|
|
.foregroundStyle(YpChatTheme.textMuted)
|
|
}
|
|
}
|
|
.padding(16)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.background(YpChatTheme.surface, in: RoundedRectangle(cornerRadius: 12))
|
|
}
|
|
}
|
|
|
|
struct PartnersDetailView: View {
|
|
@EnvironmentObject private var services: AppServices
|
|
var onBack: () -> Void
|
|
@Environment(\.openURL) private var openURL
|
|
|
|
private var chat: ChatState { services.repository.state }
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
HStack {
|
|
Button(L10n.tr("more_back"), action: onBack)
|
|
.foregroundStyle(YpChatTheme.primary700)
|
|
Text(L10n.tr("partners_title"))
|
|
.font(.title2.weight(.bold))
|
|
}
|
|
Text(L10n.tr("partners_intro"))
|
|
.foregroundStyle(YpChatTheme.textMuted)
|
|
if let err = chat.partnersError {
|
|
Text(localizeRuntimeMessage(err))
|
|
.foregroundStyle(YpChatTheme.danger)
|
|
}
|
|
ForEach(Array(chat.partnerLinks.enumerated()), id: \.offset) { _, link in
|
|
Button {
|
|
if let u = URL(string: link.url) { openURL(u) }
|
|
} label: {
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text(link.pageName).fontWeight(.semibold)
|
|
Text(link.url).foregroundStyle(YpChatTheme.primary700)
|
|
Text(L10n.tr("external_link"))
|
|
.font(.caption)
|
|
.foregroundStyle(YpChatTheme.textMuted)
|
|
}
|
|
.padding(16)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.background(YpChatTheme.surface, in: RoundedRectangle(cornerRadius: 12))
|
|
}
|
|
.buttonStyle(.plain)
|
|
}
|
|
}
|
|
.padding(16)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Chat
|
|
|
|
struct YpChatConversationView: View {
|
|
@EnvironmentObject private var services: AppServices
|
|
private var repo: ChatRepository { services.repository }
|
|
private var chat: ChatState { repo.state }
|
|
|
|
@State private var draft = ""
|
|
@State private var showSmileys = false
|
|
@State private var photoItem: PhotosPickerItem?
|
|
|
|
private static let maxImageBytes = 5 * 1024 * 1024
|
|
|
|
var body: some View {
|
|
VStack(spacing: 0) {
|
|
HStack {
|
|
Button(L10n.tr("back")) { repo.closeConversation() }
|
|
Text(chat.currentConversation ?? "")
|
|
.fontWeight(.bold)
|
|
.frame(maxWidth: .infinity)
|
|
Button(L10n.tr("block")) {
|
|
if let u = chat.currentConversation { repo.blockUser(userName: u) }
|
|
}
|
|
Button(L10n.tr("unblock")) {
|
|
if let u = chat.currentConversation { repo.unblockUser(userName: u) }
|
|
}
|
|
}
|
|
.padding(12)
|
|
Divider()
|
|
ScrollView {
|
|
LazyVStack(alignment: .leading, spacing: 8) {
|
|
ForEach(Array(chat.messages.enumerated()), id: \.offset) { _, message in
|
|
messageBubble(message)
|
|
}
|
|
}
|
|
.padding(12)
|
|
}
|
|
if chat.isUploadingImage || chat.imageUploadMessage != nil {
|
|
uploadBanner
|
|
}
|
|
HStack(alignment: .bottom) {
|
|
TextField(L10n.tr("message_placeholder"), text: $draft, axis: .vertical)
|
|
.lineLimit(1 ... 4)
|
|
.textFieldStyle(.roundedBorder)
|
|
.disabled(chat.isUploadingImage)
|
|
Button {
|
|
showSmileys.toggle()
|
|
} label: {
|
|
Image(systemName: "face.smiling")
|
|
}
|
|
.disabled(chat.isUploadingImage)
|
|
PhotosPicker(selection: $photoItem, matching: .images) {
|
|
Image(systemName: "photo")
|
|
}
|
|
.disabled(chat.isUploadingImage)
|
|
Button(L10n.tr("button_send")) {
|
|
repo.sendMessage(text: draft)
|
|
draft = ""
|
|
}
|
|
.buttonStyle(.borderedProminent)
|
|
.tint(YpChatTheme.primary600)
|
|
.disabled(draft.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || chat.isUploadingImage)
|
|
}
|
|
.padding(12)
|
|
if showSmileys {
|
|
ScrollView(.horizontal, showsIndicators: false) {
|
|
HStack(spacing: 8) {
|
|
ForEach(ypChatSmileys) { sm in
|
|
Button {
|
|
draft += sm.token
|
|
showSmileys = false
|
|
} label: {
|
|
Text(smileyEmoji(hexCode: sm.hexCode))
|
|
.padding(.horizontal, 12)
|
|
.padding(.vertical, 8)
|
|
.background(YpChatTheme.primary100, in: Capsule())
|
|
.foregroundStyle(YpChatTheme.primary700)
|
|
.fontWeight(.bold)
|
|
}
|
|
}
|
|
}
|
|
.padding(.horizontal, 12)
|
|
}
|
|
}
|
|
if let err = chat.errorMessage {
|
|
Text(localizeRuntimeMessage(err))
|
|
.font(.footnote)
|
|
.foregroundStyle(YpChatTheme.danger)
|
|
.padding(.horizontal, 12)
|
|
}
|
|
}
|
|
.onChange(of: photoItem) { _, new in
|
|
Task { await handlePhoto(new) }
|
|
}
|
|
}
|
|
|
|
private var uploadBanner: some View {
|
|
let msg: String = {
|
|
if chat.isUploadingImage { return L10n.tr("image_upload_in_progress") }
|
|
return localizeRuntimeMessage(chat.imageUploadMessage ?? "")
|
|
}()
|
|
let ok = chat.imageUploadMessage == "Image uploaded"
|
|
let bg: Color = chat.isUploadingImage ? YpChatTheme.surfaceSoftBlue : (ok ? YpChatTheme.surfaceSoftGreen : YpChatTheme.surfaceSoftRed)
|
|
let fg: Color = chat.isUploadingImage || ok ? YpChatTheme.primary700 : YpChatTheme.danger
|
|
return Text(msg)
|
|
.font(.subheadline)
|
|
.foregroundStyle(fg)
|
|
.padding(.horizontal, 14)
|
|
.padding(.vertical, 10)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.background(bg)
|
|
.padding(.horizontal, 12)
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func messageBubble(_ message: ChatMessageDto) -> some View {
|
|
let selfMsg = message.from == (chat.currentUser?.userName ?? "")
|
|
HStack {
|
|
if selfMsg { Spacer(minLength: 40) }
|
|
VStack(alignment: selfMsg ? .trailing : .leading) {
|
|
if message.isImage, let urlStr = message.imageUrl, let url = URL(string: urlStr) {
|
|
AsyncImage(url: url) { phase in
|
|
switch phase {
|
|
case .success(let img):
|
|
img.resizable().scaledToFit().frame(maxHeight: 220)
|
|
case .failure:
|
|
Text(L10n.tr("image_message")).foregroundStyle(YpChatTheme.textMuted)
|
|
case .empty:
|
|
ProgressView()
|
|
@unknown default:
|
|
EmptyView()
|
|
}
|
|
}
|
|
} else {
|
|
Text(message.message)
|
|
}
|
|
}
|
|
.padding(12)
|
|
.background(selfMsg ? YpChatTheme.bubbleSelf : YpChatTheme.surfaceSubtle, in: RoundedRectangle(cornerRadius: 18))
|
|
if !selfMsg { Spacer(minLength: 40) }
|
|
}
|
|
}
|
|
|
|
private func handlePhoto(_ item: PhotosPickerItem?) async {
|
|
guard let item, let partner = chat.currentConversation else { return }
|
|
repo.setImageUploadState(inProgress: true, message: nil)
|
|
guard let data = try? await item.loadTransferable(type: Data.self) else {
|
|
repo.setImageUploadState(inProgress: false, message: "Image could not be opened")
|
|
photoItem = nil
|
|
return
|
|
}
|
|
guard data.count <= Self.maxImageBytes else {
|
|
repo.setImageUploadState(inProgress: false, message: "Image exceeds 5 MB")
|
|
photoItem = nil
|
|
return
|
|
}
|
|
do {
|
|
let response = try await repo.uploadImage(data: data, fileName: "ypchat-image.jpg", mimeType: "image/jpeg")
|
|
if response.success, let code = response.code, let url = response.url, !code.isEmpty {
|
|
repo.sendImage(toUserName: partner, imageCode: code, imageUrl: url)
|
|
repo.setImageUploadState(inProgress: false, message: "Image uploaded")
|
|
} else {
|
|
repo.setImageUploadState(inProgress: false, message: response.error ?? "Image upload failed")
|
|
}
|
|
} catch {
|
|
repo.setImageUploadState(inProgress: false, message: error.localizedDescription)
|
|
}
|
|
photoItem = nil
|
|
}
|
|
}
|