Skip to content

[trello.com/c/7YVA2NTe] cancel files loading update #850

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 39 additions & 8 deletions Adamant/Modules/Chat/View/Helpers/CircularProgressView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@

import SwiftUI
import UIKit
import Combine

final class CircularProgressState: ObservableObject {
let lineWidth: CGFloat
let backgroundColor: UIColor
let progressColor: UIColor
@Published var progress: Double = 0
@Published var hidden: Bool = false

@Published var isSpinning: Bool = false
@Published var backgroundGradient: LinearGradient? = nil

init(
lineWidth: CGFloat = 6,
backgroundColor: UIColor = .lightGray,
Expand All @@ -33,18 +36,31 @@ final class CircularProgressState: ObservableObject {

struct CircularProgressView: View {
@StateObject private var state: CircularProgressState

@State private var rotationAngle: Double = 0
@State private var timer: Timer.TimerPublisher = Timer.publish(every: 0.016, on: .main, in: .common)
@State private var timerCancellable: Cancellable?

init(state: @escaping () -> CircularProgressState) {
_state = .init(wrappedValue: state())
}

var body: some View {
ZStack {
Circle()
.stroke(
Color(uiColor: state.backgroundColor),
lineWidth: state.lineWidth
)
if let gradient = state.backgroundGradient {
Circle()
.stroke(
gradient,
lineWidth: state.lineWidth
)
.rotationEffect(.degrees(rotationAngle))
} else {
Circle()
.stroke(
Color(uiColor: state.backgroundColor),
lineWidth: state.lineWidth
)
}

Circle()
.trim(from: 0, to: state.progress)
.stroke(
Expand All @@ -58,5 +74,20 @@ struct CircularProgressView: View {
.animation(.easeOut, value: state.progress)
}
.opacity(state.hidden ? .zero : 1.0)
.onReceive(timer) { _ in
if state.isSpinning {
rotationAngle += 2
if rotationAngle >= 360 { rotationAngle -= 360 }
}
}
.onReceive(state.$isSpinning) { spinning in
if spinning {
timer = Timer.publish(every: 0.016, on: .main, in: .common)
timerCancellable = timer.connect()
} else {
timerCancellable?.cancel()
timerCancellable = nil
}
}
}
}
26 changes: 17 additions & 9 deletions Adamant/Modules/Chat/View/Helpers/FileMessageStatus.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,23 @@
import CommonKit
import UIKit

enum FileMessageStatus: Equatable {
case busy
case needToDownload(failed: Bool)
case failed
enum FileMessageStatus: Hashable {
case uploading
case downloading
case success
case failed
case needToDownload(failed: Bool)

var image: UIImage {
switch self {
case .busy: return .asset(named: "status_failed") ?? .init()
case .success: return .asset(named: "status_success") ?? .init()
case .failed: return .asset(named: "status_failed") ?? .init()
case .uploading:
return UIImage(systemName: "square.fill") ?? UIImage()
case .downloading:
return .asset(named: "status_pending") ?? .init()
case .success:
return .asset(named: "status_success") ?? .init()
case .failed:
return .asset(named: "status_failed") ?? .init()
case let .needToDownload(failed):
guard !failed else {
return .asset(named: "download-circular-error") ?? .init()
Expand All @@ -30,8 +36,10 @@ enum FileMessageStatus: Equatable {

var imageTintColor: UIColor {
switch self {
case .busy, .needToDownload, .success: return .adamant.primary
case .failed: return .adamant.attention
case .uploading, .downloading, .needToDownload, .success:
return .adamant.primary
case .failed:
return .adamant.attention
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ final class ChatMediaContainerView: UIView {

private var statusProgressState = CircularProgressState(
lineWidth: 2.0,
backgroundColor: .clear,
backgroundColor: .lightGray,
progressColor: .adamant.primary,
progress: .zero,
hidden: true
Expand Down Expand Up @@ -143,7 +143,7 @@ final class ChatMediaContainerView: UIView {
}

@objc func onStatusButtonTap() {
if model.status == .busy {
if model.status == .uploading {
actionHandler(.cancelUploading(messageId: model.id))
return
}
Expand Down Expand Up @@ -185,7 +185,7 @@ extension ChatMediaContainerView {
reactionsStack.insertSubview(progressRingHostingView, aboveSubview: statusButton)
progressRingHostingView.snp.makeConstraints { make in
make.center.equalTo(statusButton)
make.size.equalTo(31)
make.size.equalTo(30)
}
}

Expand Down Expand Up @@ -222,9 +222,18 @@ extension ChatMediaContainerView {

func updateProgressRing() {
let averageProgress = averageUploadProgress()


if averageProgress == 0 {
statusProgressState.backgroundGradient = LinearGradient(
gradient: Gradient(colors: [.white, .black]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)} else {
statusProgressState.backgroundGradient = nil
}
statusProgressState.progress = averageProgress / 100
statusProgressState.hidden = averageProgress == 0 || averageProgress == 100
statusProgressState.hidden = averageProgress == 100 || !(model.status == .uploading)
statusProgressState.isSpinning = (model.status == .uploading && averageProgress == 0)
}

func updateLayout() {
Expand Down
7 changes: 4 additions & 3 deletions Adamant/Modules/Chat/ViewModel/ChatFileService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ final class ChatFileService: ChatFileProtocol, Sendable {
func cancelUpload(messageId: String) async {
guard let tasks = uploadTasks[messageId] else { return }

uploadTasks[messageId] = nil
uploadingFilesDictionary[messageId] = nil

var fileIdsToRemove: [String] = []

for (fileId, task) in tasks {
Expand All @@ -221,9 +224,6 @@ final class ChatFileService: ChatFileProtocol, Sendable {
oldIds: fileIdsToRemove,
txId: messageId
)

uploadTasks[messageId] = nil
uploadingFilesDictionary[messageId] = nil
}

func isDownloadPreviewLimitReached(for fileId: String) -> Bool {
Expand Down Expand Up @@ -993,6 +993,7 @@ extension ChatFileService {

do {
let result = try await uploadTask.value
try Task.checkCancellation()

sendProgress(
for: result.file.cid,
Expand Down
8 changes: 6 additions & 2 deletions Adamant/Modules/Chat/ViewModel/ChatViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1728,8 +1728,12 @@ extension ChatViewModel {
return .failed
}

if model.content.fileModel.files.first(where: { $0.isBusy }) != nil {
return .busy
if model.content.fileModel.files.contains(where: { $0.isUploading }) {
return .uploading
}

if model.content.fileModel.files.contains(where: { $0.isDownloading }) {
return .downloading
}

if model.content.fileModel.files.contains(where: {
Expand Down
8 changes: 6 additions & 2 deletions Adamant/Services/DataProviders/AdamantChatsProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1357,8 +1357,12 @@ extension AdamantChatsProvider {

do {
let locallyID = signedTransaction.generateId() ?? UUID().uuidString
transaction.transactionId = locallyID
transaction.chatMessageId = locallyID
if transaction.transactionId.isEmpty {
transaction.transactionId = locallyID
}
if transaction.chatMessageId?.isEmpty ?? true {
transaction.chatMessageId = locallyID
}

let id = try await apiService.sendMessageTransaction(transaction: signedTransaction).get()

Expand Down
Loading