mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
Read Receipts sheet + enabled RR by default (#2123)
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// Copyright 2023 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ReadReceiptCell: View {
|
||||
let readReceipt: ReadReceipt
|
||||
let memberState: RoomMemberState?
|
||||
let imageProvider: ImageProviderProtocol?
|
||||
|
||||
private var title: String {
|
||||
memberState?.displayName ?? readReceipt.userID
|
||||
}
|
||||
|
||||
private var subtitle: String {
|
||||
guard title != readReceipt.userID else {
|
||||
return ""
|
||||
}
|
||||
return readReceipt.userID
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 12) {
|
||||
LoadableAvatarImage(url: memberState?.avatarURL,
|
||||
name: memberState?.displayName,
|
||||
contentID: readReceipt.userID,
|
||||
avatarSize: .user(on: .readReceiptSheet),
|
||||
imageProvider: imageProvider)
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
HStack(spacing: 12) {
|
||||
Text(title)
|
||||
.font(.compound.bodyMDSemibold)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
.lineLimit(1)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
if let formattedTimestamp = readReceipt.formattedTimestamp {
|
||||
Text(formattedTimestamp)
|
||||
.font(.compound.bodyXS)
|
||||
.foregroundColor(.compound.textSecondary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
Text(subtitle)
|
||||
.font(.compound.bodySM)
|
||||
.foregroundColor(.compound.textSecondary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
.padding(.horizontal, 16)
|
||||
}
|
||||
}
|
||||
|
||||
struct ReadReceiptCell_Previews: PreviewProvider, TestablePreview {
|
||||
static var previews: some View {
|
||||
ReadReceiptCell(readReceipt: .init(userID: "@test:matrix.org",
|
||||
formattedTimestamp: "10:00"),
|
||||
memberState: .init(displayName: "Test",
|
||||
avatarURL: nil),
|
||||
imageProvider: MockMediaProvider())
|
||||
.previewDisplayName("No Image")
|
||||
ReadReceiptCell(readReceipt: .init(userID: "@test:matrix.org",
|
||||
formattedTimestamp: "10:00"),
|
||||
memberState: .init(displayName: "Test",
|
||||
avatarURL: URL.documentsDirectory),
|
||||
imageProvider: MockMediaProvider())
|
||||
.previewDisplayName("With Image")
|
||||
ReadReceiptCell(readReceipt: .init(userID: "@test:matrix.org",
|
||||
formattedTimestamp: "10:00"),
|
||||
memberState: nil,
|
||||
imageProvider: MockMediaProvider())
|
||||
.previewDisplayName("Loading Member")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// Copyright 2023 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ReadReceiptsSummaryView: View {
|
||||
let orderedReadReceipts: [ReadReceipt]
|
||||
@EnvironmentObject private var context: RoomScreenViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
Text(L10n.commonSeenBy)
|
||||
.font(.compound.bodyLGSemibold)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
.padding(.horizontal, 16)
|
||||
ScrollView {
|
||||
LazyVStack(spacing: 0) {
|
||||
ForEach(orderedReadReceipts) { receipt in
|
||||
ReadReceiptCell(readReceipt: receipt,
|
||||
memberState: context.viewState.members[receipt.userID],
|
||||
imageProvider: context.imageProvider)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.top, 24)
|
||||
.presentationDetents([.medium, .large])
|
||||
.presentationBackground(Color.compound.bgCanvasDefault)
|
||||
.presentationDragIndicator(.visible)
|
||||
}
|
||||
}
|
||||
|
||||
struct ReadReceiptsSummaryView_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = {
|
||||
let members: [RoomMemberProxyMock] = [
|
||||
.mockAlice,
|
||||
.mockBob,
|
||||
.mockCharlie,
|
||||
.mockDan
|
||||
]
|
||||
let roomProxyMock = RoomProxyMock(with: .init(displayName: "Room", members: members))
|
||||
let mock = RoomScreenViewModel(roomProxy: roomProxyMock,
|
||||
timelineController: MockRoomTimelineController(),
|
||||
mediaProvider: MockMediaProvider(),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
application: ApplicationMock(),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
notificationCenter: NotificationCenterMock())
|
||||
return mock
|
||||
}()
|
||||
|
||||
static let orderedReadReceipts: [ReadReceipt] = [
|
||||
.init(userID: "@alice:matrix.org", formattedTimestamp: "10:00"),
|
||||
.init(userID: "@bob:matrix.org", formattedTimestamp: "9:30"),
|
||||
.init(userID: "@charlie:matrix.org", formattedTimestamp: "9:00"),
|
||||
.init(userID: "@dan:matrix.org", formattedTimestamp: "8:30"),
|
||||
.init(userID: "@loading:matrix.org", formattedTimestamp: "Long time ago")
|
||||
]
|
||||
|
||||
static var previews: some View {
|
||||
ReadReceiptsSummaryView(orderedReadReceipts: orderedReadReceipts)
|
||||
.environmentObject(viewModel.context)
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,10 @@ struct RoomScreen: View {
|
||||
ReactionsSummaryView(reactions: $0.reactions, members: context.viewState.members, imageProvider: context.imageProvider, selectedReactionKey: $0.selectedKey)
|
||||
.edgesIgnoringSafeArea([.bottom])
|
||||
}
|
||||
.sheet(item: $context.readReceiptsSummaryInfo) {
|
||||
ReadReceiptsSummaryView(orderedReadReceipts: $0.orderedReceipts)
|
||||
.environmentObject(context)
|
||||
}
|
||||
.interactiveQuickLook(item: $context.mediaPreviewItem)
|
||||
.track(screen: .room)
|
||||
.onDrop(of: ["public.item", "public.file-url"], isTargeted: $dragOver) { providers -> Bool in
|
||||
|
||||
@@ -45,8 +45,12 @@ struct TimelineReadReceiptsView: View {
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
context.send(viewAction: .showReadReceipts(itemID: timelineItem.id))
|
||||
}
|
||||
.accessibilityElement(children: .ignore)
|
||||
.accessibilityLabel(accessibilityLabel)
|
||||
.accessibilityHint(L10n.a11yReadReceiptsTapToShowAll)
|
||||
}
|
||||
|
||||
private var remaining: Int {
|
||||
|
||||
Reference in New Issue
Block a user