Issue / Feature request Attachment Progress
This is a really a feature request that I'm happy to implement.
There is currently no way to show that an attachment is still being uploaded so in progress this is on the recipients end, the best current workflow i can see for sending video attachments is.
- Send a thumbnail of a video to the resource server
- In a background task start the video upload to the resource server
- Send the recipient the message with the thumbnail url and the video url
This means that the thumbnails for the videos are correctly show in the message view however when the recipient tries to view the video attachment they are shown:
What I think should happen is that the attachment should have a status type attached, so something like uploading, ready then
- Send a thumbnail of a video to the resource server
- In a background task start the video upload to the resource server
- Send the recipient the message with the thumbnail url and the video url, attachments of this message have the status uploading
- Once upload is complete another message is sent with the ready status for attachments
The AttachmentsPage can then show a loading / in progress image instead of not available, the thumbnail could also have a progress spinner overlay to show it is being uploaded.
Would be good to get some views on this, or if anyone has any other ideas on how this scenario could be handled
Hey @fred-bowker, nice idea, if I understood correctly, to achieve this, we can add a status field to Attachment model. maybe like this:
enum UploadingStatus { case notStarted, thumbnailInProgress(Int), videoInProgress(Int), uploaded }
and then the user can pass actual uploading progress to this field in his messages array. for images videoInProgress will be skipped, and uploaded will be set immediately after uploading the image. we can also add default support of this to the default library cell, also forbidding tap on attachment during thumbnailInProgress, allowing to see fullscreen thumbnail during videoInProgress, and only then opening the video when it's ready as you suggested.
Would be amazing to have this feature, thank you for putting thought into this, have a wonderful day!
Hi @f3dm76, my initial idea for this was to get something in that works and solves the current problem of the receiving user having no idea that the upload is in progress.
For the upload progress status indicator in MB videoInProgress(Int), this is a fairly complex piece of work as you would need to constantly call the receiver with upload information and then the receiver would need to update the progress indicator so it was smooth. If you were uploading 10 files of say 50MB then that is 500 calls at 1MB a second. I think to do this the receiver would need to have it's own progress indicator then readjust when it got a status update say every 10MB. For the initial version I think we can leave out the int value on videoInProgress(Int)
For thumbnail uploads, the thumbnails can be uploaded to the resource server before sending the attachment message, the upload to the resource server of all thumbnails area small <2MB so this will be quick and it means that the recipient will always have thumbnails for then displaying the progress on.
For an initial version of this i think we go with your idea of having an enum UploadingStatus and it just has uploading and ready. This gives us
enum UploadingStatus { case uploading case ready }
Attachment { uploadingStatus: UploadingStatus }
Let me know what you think and have a top day!
I think we can add all the cases, since it's up to the calling person to use or not use them. To not force anything on the user, we can make it optional .videoInProgress(Int?) maybe, what do you think? So the user can just set nil if he doesn't want to implement real progress tracking, and the library can just show abstract uploading state if the value is nil.
Also it doesn't have to be updated constantly I think - can just update every 10% for example. But yeah, you are right in that the user shouldn't have to do it if they don't need it.
I'd like to introduce the cases all at once, so we won't need to change the API later. We could mark them as // todo for now, but just so that API is ready for later improvements
For the progress indicator, you probably want it to show some sort of smooth indicator so not jump every 10MB, I think what would need to happen is that the receiver has some sort of artificial indicator which is showing 1MB updates, then it gets readjusted say every 10MB, this would reduce the network calls, if you have 10 100MB files then that is still 100 calls for progress indicators. I think this is doable just a bit complex for the gain it gives and not really a priority for implementing hence why I think go with the simplest option first.
I'd be ok to go with, your Int? suggestion, then implement status MB indicators at some future point and include a TODO
enum UploadingStatus { case uploading(Int?) // images and videos as image uploads could also be large case ready }
It's up to the user, both to track uploadings' progress and decide how often he wants the updates to come. It's also up to him to display the indicator if he uses a message cell builder. For our library built-in cell we can just interpolate between the updates however often they come to make it look smooth. But basically the complicated part is on user's side, and what we need to do here now is make a convenient expandable API to cover all potential cases. We don't have to implement everything yet, but I think we need to declare all of them.
So, in this API it's important to:
- distinguish between thumbnail uploading and the main video uploading (so that potentially down the road we can show the thumb while the video is loading)
- allow to pass exact percentage of the uploading, to be able to do as we please later down the road
- provide a less demanding way of tracing simple progress - inProgress/done (what you suggested initially)
- make it easy to use as much of the possibilities as you need. meaning if you don't need the percentages, we should make it easy to ignore them. or if you need to, the API is there for you
We could just declare enum UploadingStatus { case uploading case ready } but, if we add other cases later, 'uploading' would stop making sense. Basically I just want to settle on the API now without needing to change it down the road, renaming cases, and deprecating stuff. Even if only the simple ones work now, their names should already "keep in mind" that the enum would be extended later.
That's why I suggested the enum which I think covers all of those cases, I'll just make them optional, and maybe a convenience var: enum UploadingStatus { case notStarted case thumbnailInProgress(Int?) case videoInProgress(Int?) case done
var isUploading: Bool { return self == .thumbnailInProgress || self == .videoInProgress } }
In any case, if you feel like that's an overkill, go with whatever you feel right. Have a brilliant weekend!
what I'm saying is I don't think the complexity is on the users side, to get a smooth progress indicator you would need to implement something in the receiving side, sure we could add the api for this but then it's not really going to fully work as with your suggestion for 100% you would need to do 100 api calls.
My suggestion is to get something working first then handle the status indicator, we can leave TODO: notes in the code but with no prioritisation and no way to decide who is actually writing the code, then why not just go with the simplest interface that works then update the api later.
thumbnailInProgress with my suggestion isn't needed as you upload thumbnails before the call, that is the simpler option, videoInProgress(Int?) that doesn't make sense as you have image (non thumbnail) and video uploads so why just videoInProgress.
I'll go with
enum UploadingStatus { case uploading(Int?) // images and videos as image uploads could also be large case ready }
as that still allows to implement a status indicator once the work is to be carried out and more defined
tbh, I don't get what you mean about 100 api calls. if you use smth like this to track uploading progress https://www.strv.com/blog/how-to-upload-files-using-combine-in-ios-track-their-progress-engineering. basically like this
api.uploadTrackingProgress { progress, status }
all that's left for the user is to find the message in question, find attachment in question inside this message, and update enum's attached value with the percentage. by Int I meant percentage 0...100, but we could use CGFloat 0...1, which is probably more universal and thus correct.
To get a fairly smooth indicator, I paint a circle, and update its endRadiant withAnimation depending on that.
but anyway, let's go with enum UploadingStatus { case notStarted case uploading(CGFloat?) // 0...1 case done } but please include notStarted as default value
the way I see the usage of this is like this
class ViewModel {
@Published var messages: [Message]
func sendMessage(draftMessage: DraftMessage) {
var message = draftMessage.toMessage() // without attachments yet
message.status = .sending
messages.append(message)
// sending message...
message.status = .sent
// update this message in messages
for a in message.attachments {
api.uploadTrackingProgress(a) { progress, status
a.uploadingStatus = .uploading(progress)
// update this attachment in message
// update this message in messages
}
}
}
}
struct View: View {
var body: some View {
ChatView(viewModel.message) {
viewModel.sendMessage($0)
}
}
}
I know how to write a progress update indicator but thanks for the links,
The complexity I am talking about in my previous comments is for the receiver of the attachments message, it is the receiver that the upload status is important, as when they receive a video they do not know when it is ready for viewing and it's upload state (not downloading that is different).
For the receiver to get the upload status of a large file then the device that is sending the attachment must send the upload status to the receiver multiple times while the upload is in progress, that is where i get 100 calls from, this is what my previous comments refer to
aaah, got it, thanks for the explanation. well, I don't remember seeing this in popular chat apps. usually the progress is just displayed for the one doing the uploading. and the one waiting for the medias just sees them appearing once they are fully uploaded. and you want to show them a thumbnail while they wait. and I want to show the progress to the one uploading. then your enum makes more sense for your task, sorry for the confusion
Hi Alicia, I've put up a pull request for this change. The mediaPicker also has a pull request that is related to this change https://github.com/exyte/MediaPicker/pull/47