Files
element-x-ios/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift
T
Stefan Ceriu f040964d3f Allow the user to forward the same message to multiple rooms at once
This will send the item to the rooms in order they were chosen and only deep link
into the target room if single, otherwise it will just dismiss the forwarding screen
after sending.
2026-05-29 14:27:42 +03:00

110 lines
4.2 KiB
Swift

//
// Copyright 2025 Element Creations Ltd.
// Copyright 2022-2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import Compound
import SwiftUI
struct MessageForwardingScreen: View {
@ObservedObject var context: MessageForwardingScreenViewModel.Context
var body: some View {
Form {
Section {
ForEach(context.viewState.rooms) { room in
MessageForwardingListRow(room: room,
isSelected: context.viewState.selectedRoomIDs.contains(room.id),
context: context)
}
// Replace these with ScrollView's `scrollPosition` when dropping iOS 16.
} header: {
emptyRectangle
.onAppear {
context.send(viewAction: .reachedTop)
}
} footer: {
emptyRectangle
.onAppear {
context.send(viewAction: .reachedBottom)
}
}
}
.compoundList()
.navigationTitle(L10n.commonForwardMessage)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button(L10n.actionCancel) {
context.send(viewAction: .cancel)
}
}
ToolbarItem(placement: .confirmationAction) {
Button(L10n.actionSend) {
context.send(viewAction: .send)
}
.disabled(context.viewState.selectedRoomIDs.isEmpty)
}
}
.searchController(query: $context.searchQuery, showsCancelButton: false)
.compoundSearchField()
.disableAutocorrection(true)
}
/// The greedy size of Rectangle can create an issue with the navigation bar when the search is highlighted, so is best to use a fixed frame instead of hidden() or EmptyView()
private var emptyRectangle: some View {
Rectangle()
.frame(width: 0, height: 0)
.accessibilityHidden(true)
}
}
private struct MessageForwardingListRow: View {
@Environment(\.dynamicTypeSize) var dynamicTypeSize
let room: MessageForwardingRoom
let isSelected: Bool
let context: MessageForwardingScreenViewModel.Context
var body: some View {
ListRow(label: .avatar(title: room.title,
description: room.description,
icon: avatar),
kind: .selection(isSelected: isSelected) {
context.send(viewAction: .selectRoom(roomID: room.id))
})
}
@ViewBuilder @MainActor
var avatar: some View {
if dynamicTypeSize < .accessibility3 {
RoomAvatarImage(avatar: room.avatar,
avatarSize: .room(on: .messageForwarding),
mediaProvider: context.mediaProvider)
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
.accessibilityHidden(true)
}
}
}
// MARK: - Previews
struct MessageForwardingScreen_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
let summaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
let viewModel = MessageForwardingScreenViewModel(forwardingItem: .init(id: .randomEvent,
roomID: "",
content: .init(noHandle: .init())),
userSession: UserSessionMock(.init()),
roomSummaryProvider: summaryProvider,
userIndicatorController: UserIndicatorControllerMock())
ElementNavigationStack {
MessageForwardingScreen(context: viewModel.context)
}
}
}