Files
letro-ios/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift
Michael Kaye 23ce41ec11 Align accessibility IDs with elementX android (#567)
* Rename onboarding and login user/pass accessibility ids.

* Refactor remaining server change accessibility IDs

* Avoid accessibilityIdentifier spreading to all decorations on the textBox.

Required because otherwise there are multiple items tagged with the accessibilityId
which means we can't click on "the" item.

* Move all accessibility identifiers to `AccessibilityIdentifiers.swft`
- use same naming convention on all of them
- remove the unused ones
- fix build errors in integration tests and invalid identifiers (still broken until autodiscovery lands on rosa)

---------

Co-authored-by: Stefan Ceriu <stefanc@matrix.org>
2023-02-15 07:53:04 +02:00

176 lines
6.2 KiB
Swift

//
// Copyright 2022 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 HomeScreenRoomCell: View {
let room: HomeScreenRoom
let context: HomeScreenViewModel.Context
var body: some View {
Button {
if let roomId = room.roomId {
context.send(viewAction: .selectRoom(roomIdentifier: roomId))
}
} label: {
HStack(spacing: 16.0) {
avatar
VStack(alignment: .leading, spacing: 2) {
header
footer
}
}
.frame(minHeight: 84.0)
.accessibilityElement(children: .combine)
}
.buttonStyle(HomeScreenRoomCellButtonStyle())
.accessibilityIdentifier(A11yIdentifiers.homeScreen.roomName(room.name))
.overlay(alignment: .bottom) {
Divider()
.frame(height: 0.5)
.background(Color.element.quinaryContent)
.padding(.leading, 84)
}
}
@ViewBuilder
var avatar: some View {
LoadableAvatarImage(url: room.avatarURL,
name: room.name,
contentID: room.roomId,
avatarSize: .room(on: .home),
imageProvider: context.imageProvider)
.accessibilityHidden(true)
}
@ViewBuilder
var header: some View {
HStack(alignment: .top, spacing: 16) {
Text(room.name)
.font(.element.headline)
.foregroundColor(.element.primaryContent)
.lineLimit(1)
.frame(maxWidth: .infinity, alignment: .leading)
if let timestamp = room.timestamp {
Text(timestamp)
.font(.element.footnote)
.foregroundColor(room.hasUnreads ? .element.brand : .element.tertiaryContent)
}
}
}
@ViewBuilder
var footer: some View {
HStack(alignment: .firstTextBaseline) {
ZStack(alignment: .topLeading) {
// Hidden text with 2 lines to maintain consistent height, scaling with dynamic text.
Text(" \n ").lastMessageFormatting().hidden()
switch room.lastMessage {
case .loaded(let lastMessage):
Text(lastMessage)
.lastMessageFormatting()
case .loading:
Text(HomeScreenRoom.placeholderLastMessage)
.lastMessageFormatting()
.redacted(reason: .placeholder)
case .unknown:
Text(ElementL10n.message)
.lastMessageFormatting()
}
}
Spacer()
if room.hasUnreads {
Rectangle()
.frame(width: 12, height: 12)
.foregroundColor(.element.brand)
.clipShape(Circle())
.padding(.leading, 12)
} else {
// Force extra padding between last message text and the right border of the screen if there is no unread dot
Rectangle()
.fill(Color.clear)
.frame(width: 12, height: 12)
}
}
}
}
struct HomeScreenRoomCellButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.roomCellBackground(configuration.isPressed ? .element.system : .clear)
.contentShape(Rectangle())
}
}
private extension View {
func lastMessageFormatting() -> some View {
font(.element.subheadline)
.foregroundColor(.element.tertiaryContent)
.lineLimit(2)
.multilineTextAlignment(.leading)
}
// To be used to indicate the selected room too
func roomCellBackground(_ background: Color) -> some View {
padding(.horizontal, 8)
.padding(.horizontal, 8)
.background { background }
}
}
struct HomeScreenRoomCell_Previews: PreviewProvider {
static var previews: some View {
body.tint(.element.accent)
}
static var body: some View {
let summaryProvider = MockRoomSummaryProvider(state: .loaded)
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "John Doe", roomSummaryProvider: summaryProvider),
mediaProvider: MockMediaProvider())
let viewModel = HomeScreenViewModel(userSession: userSession,
attributedStringBuilder: AttributedStringBuilder())
let rooms: [HomeScreenRoom] = summaryProvider.roomListPublisher.value.compactMap { summary -> HomeScreenRoom? in
switch summary {
case .empty:
return nil
case .filled(let details), .invalidated(let details):
return HomeScreenRoom(id: UUID().uuidString,
roomId: details.id,
name: details.name,
hasUnreads: details.unreadNotificationCount > 0,
timestamp: Date.now.formattedMinimal(),
lastMessage: .init(attributedString: details.lastMessage,
isLoading: false))
}
}
return VStack(spacing: 0) {
ForEach(rooms) { room in
HomeScreenRoomCell(room: room, context: viewModel.context)
}
}
}
}