Media loading flow changes (#483)

* Use an imageProvider directly from the view in the home screen

* Add support for media request coalescing

* Rename MediaProxy to MediaLoader

* Add new image loading mechanism to the room details screen avatar.

* Use the `SettingsScreen` prefix for all settings screen related components

* Add new image loading mechanism to the room header

* Add new image loading mechanism to the room member details screen

* Introduce a LoadableImage SwiftUI view that will automatically handle image loading

* Adopt the new LoadableImage where possible

* Fix LoadableImage not using/storing loaded images properly

* Simplify media loader enqueueing

* Made LodableImage load content after mediaSource updates. Adopt it on the home and settings screens

* Introduce a LoadableAvatarImage component and reuse it throughout the app

* Small logging tweaks, made some LoadableImage properties private

* Fix redacted skeletons avatar background color

* Fix placeholder avatars changing when backgrounding the app

* PR comments.

- Trim the @ sign off of mxid placeholders.
- Only expose AvatarSize on the avatar image, use CGSize elsewhere.

Co-authored-by: Doug <douglase@element.io>
This commit is contained in:
Stefan Ceriu
2023-01-25 19:45:01 +02:00
committed by GitHub
parent 3a74f13097
commit bd4c0b72cb
58 changed files with 875 additions and 830 deletions
@@ -46,5 +46,4 @@ struct RoomMemberDetailsViewStateBindings {
enum RoomMemberDetailsViewAction {
case selectMember(id: String)
case loadMemberData(id: String)
}
@@ -27,7 +27,8 @@ class RoomMemberDetailsViewModel: RoomMemberDetailsViewModelType, RoomMemberDeta
members: [RoomMemberProxy]) {
self.mediaProvider = mediaProvider
super.init(initialViewState: .init(members: members.map { RoomDetailsMember(withProxy: $0) },
bindings: .init()))
bindings: .init()),
imageProvider: mediaProvider)
}
// MARK: - Public
@@ -36,30 +37,6 @@ class RoomMemberDetailsViewModel: RoomMemberDetailsViewModelType, RoomMemberDeta
switch viewAction {
case .selectMember(let id):
MXLog.debug("Member selected: \(id)")
case .loadMemberData(let id):
await loadAvatar(forMember: id)
}
}
private func loadAvatar(forMember memberId: String) async {
guard let member = state.members.first(where: { $0.id == memberId }) else {
return
}
if member.avatar != nil {
// Avatar already loaded.
return
}
guard let avatarURL = member.avatarURL else {
return
}
switch await mediaProvider.loadImageFromURL(avatarURL, avatarSize: .user(on: .roomDetails)) {
case .success(let image):
if let index = state.members.firstIndex(where: { $0.id == memberId }) {
state.members[index].avatar = image
}
case .failure(let error):
MXLog.error("Failed to retrieve room member avatar: \(error)")
}
}
}
@@ -27,19 +27,12 @@ struct RoomMemberDetailsMemberCell: View {
context.send(viewAction: .selectMember(id: member.id))
} label: {
HStack {
if let avatar = member.avatar {
Image(uiImage: avatar)
.resizable()
.scaledToFill()
.frame(width: avatarSize, height: avatarSize)
.clipShape(Circle())
.accessibilityHidden(true)
} else {
PlaceholderAvatarImage(text: member.name ?? "", contentId: member.id)
.clipShape(Circle())
.frame(width: avatarSize, height: avatarSize)
.accessibilityHidden(true)
}
LoadableAvatarImage(url: member.avatarURL,
name: member.name ?? "",
contentID: member.id,
avatarSize: .user(on: .roomDetails),
imageProvider: context.imageProvider)
.accessibilityHidden(true)
Text(member.name ?? "")
.font(.element.callout.bold())
@@ -49,9 +42,6 @@ struct RoomMemberDetailsMemberCell: View {
Spacer()
}
.accessibilityElement(children: .combine)
.task {
context.send(viewAction: .loadMemberData(id: member.id))
}
}
}
}