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
@@ -36,6 +36,10 @@ class ViewModelContext<ViewState: BindableState, ViewAction>: ObservableObject {
/// Get-able/Observable `Published` property for the `ViewState`
@Published fileprivate(set) var viewState: ViewState
/// An optional image loading service so that views can manage themselves
/// Intentionally non-generic so that it doesn't grow uncontrollably
let imageProvider: ImageProviderProtocol?
/// Set-able/Bindable access to the bindable state.
subscript<T>(dynamicMember keyPath: WritableKeyPath<ViewState.BindStateType, T>) -> T {
@@ -43,9 +47,10 @@ class ViewModelContext<ViewState: BindableState, ViewAction>: ObservableObject {
set { viewState.bindings[keyPath: keyPath] = newValue }
}
init(initialViewState: ViewState) {
init(initialViewState: ViewState, imageProvider: ImageProviderProtocol?) {
self.viewActions = PassthroughSubject()
self.viewState = initialViewState
self.imageProvider = imageProvider
}
/// Send a `ViewAction` to the `ViewModel` for processing.
@@ -78,8 +83,8 @@ class StateStoreViewModel<State: BindableState, ViewAction> {
set { context.viewState = newValue }
}
init(initialViewState: State) {
context = Context(initialViewState: initialViewState)
init(initialViewState: State, imageProvider: ImageProviderProtocol? = nil) {
context = Context(initialViewState: initialViewState, imageProvider: imageProvider)
context.viewActions
.sink { [weak self] action in
guard let self else { return }