Skip to content

Commit

Permalink
Display avatars full screen when tapping on them from the room or mem…
Browse files Browse the repository at this point in the history
…ber detail screens
  • Loading branch information
stefanceriu committed Aug 4, 2023
1 parent 68f8bfe commit 3515494
Show file tree
Hide file tree
Showing 13 changed files with 132 additions and 22 deletions.
5 changes: 4 additions & 1 deletion ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,10 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
guard let roomProxy else {
fatalError()
}
let params = RoomMemberDetailsScreenCoordinatorParameters(roomProxy: roomProxy, roomMemberProxy: member, mediaProvider: userSession.mediaProvider)
let params = RoomMemberDetailsScreenCoordinatorParameters(roomProxy: roomProxy,
roomMemberProxy: member,
mediaProvider: userSession.mediaProvider,
userIndicatorController: userIndicatorController)
let coordinator = RoomMemberDetailsScreenCoordinator(parameters: params)

navigationStackCoordinator.push(coordinator) { [weak self] in
Expand Down
15 changes: 10 additions & 5 deletions ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,20 @@ struct AvatarHeaderView<Footer: View>: View {
let avatarSize: AvatarSize
let imageProvider: ImageProviderProtocol?
let subtitle: String?
var onAvatarTap: (() -> Void)?
@ViewBuilder var footer: () -> Footer

var body: some View {
VStack(spacing: 8.0) {
LoadableAvatarImage(url: avatarUrl,
name: name,
contentID: id,
avatarSize: avatarSize,
imageProvider: imageProvider)
Button {
onAvatarTap?()
} label: {
LoadableAvatarImage(url: avatarUrl,
name: name,
contentID: id,
avatarSize: avatarSize,
imageProvider: imageProvider)
}

Text(name ?? id)
.foregroundColor(.compound.textPrimary)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ struct RoomDetailsScreenViewStateBindings {
var alertInfo: AlertInfo<RoomDetailsScreenErrorType>?
var leaveRoomAlertItem: LeaveRoomAlertItem?
var ignoreUserRoomAlertItem: IgnoreUserAlertItem?

/// A media item that will be previewed with QuickLook.
var mediaPreviewItem: MediaPreviewItem?
}

struct LeaveRoomAlertItem: AlertProtocol {
Expand Down Expand Up @@ -174,6 +177,7 @@ enum RoomDetailsScreenViewAction {
case unignoreConfirmed
case processTapNotifications
case processToogleMuteNotifications
case displayAvatar
}

enum RoomDetailsScreenViewShortcut {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ typealias RoomDetailsScreenViewModelType = StateStoreViewModel<RoomDetailsScreen
class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScreenViewModelProtocol {
private let accountUserID: String
private let roomProxy: RoomProxyProtocol
private let mediaProvider: MediaProviderProtocol
private let userIndicatorController: UserIndicatorControllerProtocol
private let notificationSettingsProxy: NotificationSettingsProxyProtocol

Expand All @@ -41,6 +42,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
appSettings: AppSettings) {
self.accountUserID = accountUserID
self.roomProxy = roomProxy
self.mediaProvider = mediaProvider
self.userIndicatorController = userIndicatorController
self.notificationSettingsProxy = notificationSettingsProxy

Expand Down Expand Up @@ -104,6 +106,8 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
}
case .processToogleMuteNotifications:
Task { await toggleMuteNotifications() }
case .displayAvatar:
displayFullScreenAvatar()
}
}

Expand Down Expand Up @@ -260,4 +264,24 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
state.bindings.alertInfo = .init(id: .unknown)
}
}

private func displayFullScreenAvatar() {
guard let avatarURL = roomProxy.avatarURL else {
return
}

let loadingIndicatorIdentifier = "roomAvatarLoadingIndicator"
userIndicatorController.submitIndicator(UserIndicator(id: loadingIndicatorIdentifier, type: .modal, title: L10n.commonLoading, persistent: true))

Task {
defer {
userIndicatorController.retractIndicatorWithId(loadingIndicatorIdentifier)
}

// We don't actually know the mime type here, assume it's an image.
if case let .success(file) = await mediaProvider.loadFileFromSource(.init(url: avatarURL, mimeType: "image/jpeg")) {
state.bindings.mediaPreviewItem = MediaPreviewItem(file: file, title: roomProxy.roomTitle)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ struct RoomDetailsScreen: View {
}
}
.track(screen: .roomDetails)
.interactiveQuickLook(item: $context.mediaPreviewItem)
}

// MARK: - Private
Expand All @@ -76,6 +77,8 @@ struct RoomDetailsScreen: View {
avatarSize: .room(on: .details),
imageProvider: context.imageProvider,
subtitle: context.viewState.canonicalAlias) {
context.send(viewAction: .displayAvatar)
} footer: {
if !context.viewState.shortcuts.isEmpty {
headerSectionShortcuts
}
Expand All @@ -90,6 +93,8 @@ struct RoomDetailsScreen: View {
avatarSize: .user(on: .memberDetails),
imageProvider: context.imageProvider,
subtitle: recipient.id) {
context.send(viewAction: .displayAvatar)
} footer: {
if !context.viewState.shortcuts.isEmpty {
headerSectionShortcuts
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ struct RoomMemberDetailsScreenCoordinatorParameters {
let roomProxy: RoomProxyProtocol
let roomMemberProxy: RoomMemberProxyProtocol
let mediaProvider: MediaProviderProtocol
let userIndicatorController: UserIndicatorControllerProtocol
}

enum RoomMemberDetailsScreenCoordinatorAction { }
Expand All @@ -35,7 +36,8 @@ final class RoomMemberDetailsScreenCoordinator: CoordinatorProtocol {

viewModel = RoomMemberDetailsScreenViewModel(roomProxy: parameters.roomProxy,
roomMemberProxy: parameters.roomMemberProxy,
mediaProvider: parameters.mediaProvider)
mediaProvider: parameters.mediaProvider,
userIndicatorController: parameters.userIndicatorController)
}

func start() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,17 @@ struct RoomMemberDetailsScreenViewStateBindings {

var ignoreUserAlert: IgnoreUserAlertItem?
var alertInfo: AlertInfo<RoomMemberDetailsScreenError>?

/// A media item that will be previewed with QuickLook.
var mediaPreviewItem: MediaPreviewItem?
}

enum RoomMemberDetailsScreenViewAction {
case showUnignoreAlert
case showIgnoreAlert
case ignoreConfirmed
case unignoreConfirmed
case displayAvatar
}

enum RoomMemberDetailsScreenError: Hashable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,23 @@ typealias RoomMemberDetailsScreenViewModelType = StateStoreViewModel<RoomMemberD
class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, RoomMemberDetailsScreenViewModelProtocol {
private let roomProxy: RoomProxyProtocol
private let roomMemberProxy: RoomMemberProxyProtocol
private let mediaProvider: MediaProviderProtocol
private let userIndicatorController: UserIndicatorControllerProtocol

var callback: ((RoomMemberDetailsScreenViewModelAction) -> Void)?

init(roomProxy: RoomProxyProtocol, roomMemberProxy: RoomMemberProxyProtocol, mediaProvider: MediaProviderProtocol) {
init(roomProxy: RoomProxyProtocol,
roomMemberProxy: RoomMemberProxyProtocol,
mediaProvider: MediaProviderProtocol,
userIndicatorController: UserIndicatorControllerProtocol) {
self.roomProxy = roomProxy
self.roomMemberProxy = roomMemberProxy
self.mediaProvider = mediaProvider
self.userIndicatorController = userIndicatorController

let initialViewState = RoomMemberDetailsScreenViewState(details: RoomMemberDetails(withProxy: roomMemberProxy),
bindings: .init())

super.init(initialViewState: initialViewState, imageProvider: mediaProvider)
}

Expand All @@ -44,6 +53,8 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
Task { await ignoreUser() }
case .unignoreConfirmed:
Task { await unignoreUser() }
case .displayAvatar:
displayFullScreenAvatar()
}
}

Expand Down Expand Up @@ -82,4 +93,24 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
await self.roomProxy.updateMembers()
}
}

private func displayFullScreenAvatar() {
guard let avatarURL = roomMemberProxy.avatarURL else {
return
}

let loadingIndicatorIdentifier = "roomMemberAvatarLoadingIndicator"
userIndicatorController.submitIndicator(UserIndicator(id: loadingIndicatorIdentifier, type: .modal, title: L10n.commonLoading, persistent: true))

Task {
defer {
userIndicatorController.retractIndicatorWithId(loadingIndicatorIdentifier)
}

// We don't actually know the mime type here, assume it's an image.
if case let .success(file) = await mediaProvider.loadFileFromSource(.init(url: avatarURL, mimeType: "image/jpeg")) {
state.bindings.mediaPreviewItem = MediaPreviewItem(file: file, title: roomMemberProxy.displayName)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ struct RoomMemberDetailsScreen: View {
.alert(item: $context.ignoreUserAlert, actions: blockUserAlertActions, message: blockUserAlertMessage)
.alert(item: $context.alertInfo)
.track(screen: .user)
.interactiveQuickLook(item: $context.mediaPreviewItem)
}

// MARK: - Private
Expand All @@ -43,6 +44,8 @@ struct RoomMemberDetailsScreen: View {
avatarSize: .user(on: .memberDetails),
imageProvider: context.imageProvider,
subtitle: context.viewState.details.id) {
context.send(viewAction: .displayAvatar)
} footer: {
if let permalink = context.viewState.details.permalink {
HStack(spacing: 32) {
ShareLink(item: permalink) {
Expand Down Expand Up @@ -101,17 +104,26 @@ struct RoomMemberDetailsScreen_Previews: PreviewProvider {
static let roomProxyMock = RoomProxyMock(with: .init(displayName: ""))
static let otherUserViewModel = {
let member = RoomMemberProxyMock.mockDan
return RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, roomMemberProxy: member, mediaProvider: MockMediaProvider())
return RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock,
roomMemberProxy: member,
mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController)
}()

static let accountOwnerViewModel = {
let member = RoomMemberProxyMock.mockMe
return RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, roomMemberProxy: member, mediaProvider: MockMediaProvider())
return RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock,
roomMemberProxy: member,
mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController)
}()

static let ignoredUserViewModel = {
let member = RoomMemberProxyMock.mockIgnored
return RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock, roomMemberProxy: member, mediaProvider: MockMediaProvider())
return RoomMemberDetailsScreenViewModel(roomProxy: roomProxyMock,
roomMemberProxy: member,
mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController)
}()

static var previews: some View {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ final class RoomMembersListScreenCoordinator: CoordinatorProtocol {
// MARK: - Private

private func selectMember(_ member: RoomMemberProxyProtocol) {
let parameters = RoomMemberDetailsScreenCoordinatorParameters(roomProxy: parameters.roomProxy, roomMemberProxy: member, mediaProvider: parameters.mediaProvider)
let parameters = RoomMemberDetailsScreenCoordinatorParameters(roomProxy: parameters.roomProxy,
roomMemberProxy: member,
mediaProvider: parameters.mediaProvider,
userIndicatorController: ServiceLocator.shared.userIndicatorController)
let coordinator = RoomMemberDetailsScreenCoordinator(parameters: parameters)

navigationStackCoordinator?.push(coordinator)
Expand Down
15 changes: 12 additions & 3 deletions ElementX/Sources/UITests/UITestsAppCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -525,17 +525,26 @@ class MockScreen: Identifiable {
return navigationStackCoordinator
case .roomMemberDetailsAccountOwner:
let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(roomProxy: RoomProxyMock(with: .init(displayName: "")), roomMemberProxy: RoomMemberProxyMock.mockMe, mediaProvider: MockMediaProvider()))
let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(roomProxy: RoomProxyMock(with: .init(displayName: "")),
roomMemberProxy: RoomMemberProxyMock.mockMe,
mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController))
navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator
case .roomMemberDetails:
let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(roomProxy: RoomProxyMock(with: .init(displayName: "")), roomMemberProxy: RoomMemberProxyMock.mockAlice, mediaProvider: MockMediaProvider()))
let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(roomProxy: RoomProxyMock(with: .init(displayName: "")),
roomMemberProxy: RoomMemberProxyMock.mockAlice,
mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController))
navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator
case .roomMemberDetailsIgnoredUser:
let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(roomProxy: RoomProxyMock(with: .init(displayName: "")), roomMemberProxy: RoomMemberProxyMock.mockIgnored, mediaProvider: MockMediaProvider()))
let coordinator = RoomMemberDetailsScreenCoordinator(parameters: .init(roomProxy: RoomProxyMock(with: .init(displayName: "")),
roomMemberProxy: RoomMemberProxyMock.mockIgnored,
mediaProvider: MockMediaProvider(),
userIndicatorController: ServiceLocator.shared.userIndicatorController))
navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator
case .invitesWithBadges:
Expand Down
Loading

0 comments on commit 3515494

Please sign in to comment.