From 11db7de9d9cb77091748db442c4dd7b5daa4dffe Mon Sep 17 00:00:00 2001 From: Doug Date: Mon, 23 Jan 2023 23:49:54 +0000 Subject: [PATCH 1/4] Format the last message date correctly. --- ElementX.xcodeproj/project.pbxproj | 14 ++++++ ElementX/Sources/Other/Extensions/Date.swift | 43 +++++++++++++++++++ .../HomeScreen/HomeScreenViewModel.swift | 2 +- NSE/SupportingFiles/target.yml | 1 + UnitTests/Sources/DateTests.swift | 43 +++++++++++++++++++ 5 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 ElementX/Sources/Other/Extensions/Date.swift create mode 100644 UnitTests/Sources/DateTests.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 890a4b909b..140b702cf9 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -84,6 +84,7 @@ 237FC70AA257B935F53316BA /* SessionVerificationControllerProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C55D7E514F9DE4E3D72FDCAD /* SessionVerificationControllerProxy.swift */; }; 23B2CD5A06B16055BDDD0994 /* ApplicationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44D8C8431416EB8DFEC7E235 /* ApplicationTests.swift */; }; 24906A1E82D0046655958536 /* MessageComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E18CF12478983A5EB390FB26 /* MessageComposer.swift */; }; + 24A75F72EEB7561B82D726FD /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2141693488CE5446BB391964 /* Date.swift */; }; 24BDDD09A90B8BFE3793F3AA /* ClientProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6033779EB37259F27F938937 /* ClientProxyProtocol.swift */; }; 25618589E0DE0F1E95FC7B5C /* EmojiProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099F2D36C141D845A445B1E6 /* EmojiProviderTests.swift */; }; 2797C9D9BA642370F1C85D78 /* Untranslated.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = F75DF9500D69A3AAF8339E69 /* Untranslated.stringsdict */; }; @@ -391,6 +392,7 @@ C55A44C99F64A479ABA85B46 /* RoomScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */; }; C58E305C380D3ADDF7912180 /* StickerRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 818695BED971753243FEF897 /* StickerRoomTimelineItem.swift */; }; C6136E848E55D2C86BF760F5 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C789E7BFC066CF39B8AE0974 /* NetworkMonitor.swift */; }; + C6C06DDA8881260303FBA3A0 /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2141693488CE5446BB391964 /* Date.swift */; }; C74EE50257ED925C2B8EFCE6 /* MockSoftLogoutScreenState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B869438A1B52836F912A702 /* MockSoftLogoutScreenState.swift */; }; C76892321558E75101E68ED6 /* ReadableFrameModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398817652FA8ABAE0A31AC6D /* ReadableFrameModifier.swift */; }; C7B251DC896C0867C51B616D /* AnalyticsPrompt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 541542F5AC323709D8563458 /* AnalyticsPrompt.swift */; }; @@ -400,8 +402,10 @@ CB498F4E27AA0545DCEF0F6F /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 4003BC24B24C9E63D3304177 /* DeviceKit */; }; CB6BCBF28E4B76EA08C2926D /* StateRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16048D30F0438731C41F775 /* StateRoomTimelineItem.swift */; }; CB99B0FA38A4AC596F38CC13 /* KeychainControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5E94DCFEE803E5ABAE8ACCE /* KeychainControllerProtocol.swift */; }; + CC0D088F505F33A20DC5590F /* RoomStateEventStringBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEEAFB646E583655652C3D04 /* RoomStateEventStringBuilderTests.swift */; }; CC736DA1AA8F8B9FD8785009 /* ScreenshotDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5C4AF6E3885730CD560311C /* ScreenshotDetector.swift */; }; CCAA0671B46EAFD0BB528E2C /* apple_emojis_data.json in Resources */ = {isa = PBXBuildFile; fileRef = 8FC26871038FB0E4AAE22605 /* apple_emojis_data.json */; }; + CD0088B763CD970CF1CBF8CB /* DateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B5E97E9615A158C76B2AB77 /* DateTests.swift */; }; CD6A72B65D3B6076F4045C30 /* PHGPostHogConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */; }; CE1694C7BB93C3311524EF28 /* Untranslated.strings in Resources */ = {isa = PBXBuildFile; fileRef = D2F7194F440375338F8E2487 /* Untranslated.strings */; }; CE7148E80F09B7305E026AC6 /* OnboardingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1198B925F4A88DA74083662 /* OnboardingViewModel.swift */; }; @@ -605,6 +609,7 @@ 201305507D7DFD16E544563A /* EmojiLoaderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiLoaderProtocol.swift; sourceTree = ""; }; 2069C264213B9F381DF9F876 /* ta */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ta; path = ta.lproj/Localizable.stringsdict; sourceTree = ""; }; 2112A6CFEA46E672D90EBF54 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/Localizable.strings; sourceTree = ""; }; + 2141693488CE5446BB391964 /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; }; 218AB05B4E3889731959C5F1 /* EventBasedTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBasedTimelineItemProtocol.swift; sourceTree = ""; }; 21BA866267F84BF4350B0CB7 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-BR"; path = "pt-BR.lproj/Localizable.stringsdict"; sourceTree = ""; }; 227AC5D71A4CE43512062243 /* URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = ""; }; @@ -657,6 +662,7 @@ 39EBB6903EFD4236B8D11A42 /* fr-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "fr-CA"; path = "fr-CA.lproj/Localizable.stringsdict"; sourceTree = ""; }; 3ACBDC1D28EFB7789EB467E0 /* MockRoomProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomProxy.swift; sourceTree = ""; }; 3B5B535DA49C54523FF7A412 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/Localizable.strings; sourceTree = ""; }; + 3B5E97E9615A158C76B2AB77 /* DateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTests.swift; sourceTree = ""; }; 3BFDAF6918BB096C44788FC9 /* RoomDetailsScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreenUITests.swift; sourceTree = ""; }; 3C1A3D524D63815B28FA4D62 /* EmojiCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCategory.swift; sourceTree = ""; }; 3CDF9E55650D6035D6536538 /* nb-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "nb-NO"; path = "nb-NO.lproj/Localizable.stringsdict"; sourceTree = ""; }; @@ -897,6 +903,7 @@ AE225C66978648AA4AF37B45 /* te */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = te; path = te.lproj/Localizable.strings; sourceTree = ""; }; AE5DDBEBBA17973ED4638823 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Localizable.stringsdict; sourceTree = ""; }; AEC96B3DC55090BBF8876CC2 /* MockFileCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockFileCache.swift; sourceTree = ""; }; + AEEAFB646E583655652C3D04 /* RoomStateEventStringBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomStateEventStringBuilderTests.swift; sourceTree = ""; }; AF11DD57D9FACF2A757AB024 /* AnalyticsPromptUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptUITests.swift; sourceTree = ""; }; AF25E364AE85090A70AE4644 /* AttributedStringBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringBuilderTests.swift; sourceTree = ""; }; B07B937B036247F1962BBCC7 /* RoomMemberDetailsMemberCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsMemberCell.swift; sourceTree = ""; }; @@ -1423,6 +1430,7 @@ children = ( B6E89E530A8E92EC44301CA1 /* Bundle.swift */, A9FAFE1C2149E6AC8156ED2B /* Collection.swift */, + 2141693488CE5446BB391964 /* Date.swift */, 04DF593C3F7AF4B2FBAEB05D /* FileManager.swift */, E26747B3154A5DBC3A7E24A5 /* Image.swift */, 4E2245243369B99216C7D84E /* ImageCache.swift */, @@ -1686,6 +1694,7 @@ 6DFCAA239095A116976E32C4 /* BackgroundTaskTests.swift */, EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */, 7AB7ED3A898B07976F3AA90F /* BugReportViewModelTests.swift */, + 3B5E97E9615A158C76B2AB77 /* DateTests.swift */, DBFEAC3AC691CBB84983E275 /* ElementXTests.swift */, 9BF9E3E6A23180EC05F06460 /* EmojiMartJSONLoaderTests.swift */, 099F2D36C141D845A445B1E6 /* EmojiProviderTests.swift */, @@ -1705,6 +1714,7 @@ 2EFE1922F39398ABFB36DF3F /* RoomDetailsViewModelTests.swift */, EC589E641AE46EFB2962534D /* RoomMemberDetailsViewModelTests.swift */, 93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */, + AEEAFB646E583655652C3D04 /* RoomStateEventStringBuilderTests.swift */, F03C9D319676F3C0DC6B0203 /* ScreenshotDetectorTests.swift */, EDAA4472821985BF868CC21C /* ServerSelectionViewModelTests.swift */, A1C22B1B5FA3A765EADB2CC9 /* SessionVerificationStateMachineTests.swift */, @@ -2870,6 +2880,7 @@ 1B4B3E847BF944DB2C1C217F /* BackgroundTaskServiceProtocol.swift in Sources */, 9A3B0CDF097E3838FB1B9595 /* Bundle.swift in Sources */, DFCA89C4EC2A5332ED6B441F /* DataProtectionManager.swift in Sources */, + 24A75F72EEB7561B82D726FD /* Date.swift in Sources */, E8AB8D16E6D8E8E501F29BD9 /* FileCache.swift in Sources */, A33784831AD880A670CAA9F9 /* FileManager.swift in Sources */, 59F940FCBE6BC343AECEF75E /* ImageCache.swift in Sources */, @@ -2910,6 +2921,7 @@ 0F9E38A75337D0146652ACAB /* BackgroundTaskTests.swift in Sources */, 7F61F9ACD5EC9E845EF3EFBF /* BugReportServiceTests.swift in Sources */, C7CFDB4929DDD9A3B5BA085D /* BugReportViewModelTests.swift in Sources */, + CD0088B763CD970CF1CBF8CB /* DateTests.swift in Sources */, 9C45CE85325CD591DADBC4CA /* ElementXTests.swift in Sources */, 501304F26B52DF7024011B6C /* EmojiMartJSONLoaderTests.swift in Sources */, 25618589E0DE0F1E95FC7B5C /* EmojiProviderTests.swift in Sources */, @@ -2937,6 +2949,7 @@ EA974337FA7D040E7C74FE6E /* RoomDetailsViewModelTests.swift in Sources */, 6B31508C6334C617360C2EAB /* RoomMemberDetailsViewModelTests.swift in Sources */, 46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */, + CC0D088F505F33A20DC5590F /* RoomStateEventStringBuilderTests.swift in Sources */, EA31DD9043B91ECB8E45A9A6 /* ScreenshotDetectorTests.swift in Sources */, 93875ADD456142D20823ED24 /* ServerSelectionViewModelTests.swift in Sources */, 86675910612A12409262DFBD /* SessionVerificationStateMachineTests.swift in Sources */, @@ -3008,6 +3021,7 @@ C3522917C0C367C403429EEC /* CoordinatorProtocol.swift in Sources */, 12C867E85E6D12EEDFD0B127 /* CustomStringConvertible.swift in Sources */, C4F69156C31A447FEFF2A47C /* DTHTMLElement+AttributedStringBuilder.swift in Sources */, + C6C06DDA8881260303FBA3A0 /* Date.swift in Sources */, 1CF18DE71D5D23C61BD88852 /* DebugScreen.swift in Sources */, EE8491AD81F47DF3C192497B /* DecorationTimelineItemProtocol.swift in Sources */, FE4593FC2A02AAF92E089565 /* ElementAnimations.swift in Sources */, diff --git a/ElementX/Sources/Other/Extensions/Date.swift b/ElementX/Sources/Other/Extensions/Date.swift new file mode 100644 index 0000000000..d68dfc9c49 --- /dev/null +++ b/ElementX/Sources/Other/Extensions/Date.swift @@ -0,0 +1,43 @@ +// +// 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 Foundation + +extension Date { + /// The date formatted with the minimal necessary units given how long ago it occurred. + func formattedMinimal() -> String { + let calendar = Calendar.current + + if calendar.isDateInToday(self) { + // Just the time if it was today. + return formatted(date: .omitted, time: .shortened) + } else if calendar.isDateInYesterday(self) { + // Simply "Yesterday" if it was yesterday. + return formatted(Date.RelativeFormatStyle(presentation: .named, capitalizationContext: .beginningOfSentence)) + } else if let sixDaysAgo = calendar.date(byAdding: .day, value: -6, to: calendar.startOfDay(for: .now)), + sixDaysAgo <= self { + // The named day if it was in the last 6 days. + return formatted(.dateTime.weekday(.wide)) + } else if let oneYearAgo = calendar.date(byAdding: .year, value: -1, to: .now), + oneYearAgo <= self { + // The day and month if it was in the last 6 days. + return formatted(.dateTime.day().month()) + } else { + // The day, month and year if it is any older. + return formatted(.dateTime.year().day().month()) + } + } +} diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index 6b61b9698a..7ad286357a 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -261,7 +261,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol } if let lastMessageTimestamp = details.lastMessageTimestamp { - room.timestamp = lastMessageTimestamp.formatted(date: .omitted, time: .shortened) + room.timestamp = lastMessageTimestamp.formattedMinimal() } roomsForIdentifiers[details.id] = room diff --git a/NSE/SupportingFiles/target.yml b/NSE/SupportingFiles/target.yml index cf9b1bb130..9491128531 100644 --- a/NSE/SupportingFiles/target.yml +++ b/NSE/SupportingFiles/target.yml @@ -88,6 +88,7 @@ targets: - path: ../../ElementX/Sources/Other/Extensions/FileManager.swift - path: ../../ElementX/Sources/Other/Extensions/URL.swift - path: ../../ElementX/Sources/Other/Extensions/Bundle.swift + - path: ../../ElementX/Sources/Other/Extensions/Date.swift - path: ../../ElementX/Sources/Other/Extensions/ImageCache.swift - path: ../../ElementX/Sources/Other/AvatarSize.swift - path: ../../ElementX/Sources/Other/InfoPlistReader.swift diff --git a/UnitTests/Sources/DateTests.swift b/UnitTests/Sources/DateTests.swift new file mode 100644 index 0000000000..563ea0debe --- /dev/null +++ b/UnitTests/Sources/DateTests.swift @@ -0,0 +1,43 @@ +// +// 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. +// + +@testable import ElementX +import XCTest + +// swiftlint:disable force_unwrapping +class DateTests: XCTestCase { + let calendar = Calendar.current + let startOfToday = Calendar.current.startOfDay(for: .now) + let startOfYesterday = Calendar.current.startOfDay(for: Calendar.current.date(byAdding: .day, value: -1, to: .now)!) + + func testMinimalDateFormatting() { + let today = calendar.date(byAdding: DateComponents(hour: 9, minute: 30), to: startOfToday) + XCTAssertEqual(today?.formattedMinimal(), "9:30 AM") + + let yesterday = calendar.date(byAdding: .hour, value: 1, to: startOfYesterday) + XCTAssertEqual(yesterday?.formattedMinimal(), "Yesterday") + + let saturday = calendar.nextWeekend(startingAfter: startOfToday, direction: .backward)?.start + XCTAssertEqual(saturday?.formattedMinimal(), "Saturday") + + // This test will fail during the first 6 days of the year. + let newYearsDay = calendar.date(from: DateComponents(year: calendar.component(.year, from: startOfToday), month: 1, day: 1))! + XCTAssertEqual(newYearsDay.formattedMinimal(), "Jan 1") + + let theMillennium = calendar.date(from: DateComponents(year: 2000, month: 1, day: 1))! + XCTAssertEqual(theMillennium.formattedMinimal(), "Jan 1, 2000") + } +} From 3eeb37ff7331bf6ce6c5b1f744e1bd3ebbfed781 Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 24 Jan 2023 08:44:55 +0000 Subject: [PATCH 2/4] Update room cell layout. Fix an issue where the longer the date got the smaller the last message width was. --- .../Screens/HomeScreen/View/HomeScreen.swift | 6 +- .../HomeScreen/View/HomeScreenRoomCell.swift | 155 ++++++++++-------- 2 files changed, 93 insertions(+), 68 deletions(-) diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift index eb6aea5347..f975570b53 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift @@ -35,16 +35,15 @@ struct HomeScreen: View { } if context.viewState.roomListMode == .skeletons { - LazyVStack { + LazyVStack(spacing: 0) { ForEach(context.viewState.visibleRooms) { room in HomeScreenRoomCell(room: room, context: context) .redacted(reason: .placeholder) .disabled(true) } } - .padding(.horizontal) } else { - LazyVStack { + LazyVStack(spacing: 0) { ForEach(context.viewState.visibleRooms) { room in Group { if room.isPlaceholder { @@ -62,7 +61,6 @@ struct HomeScreen: View { } } } - .padding(.horizontal) .searchable(text: $context.searchQuery) .disableAutocorrection(true) } diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift index aa3293a28a..05ddf7a483 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift @@ -29,60 +29,11 @@ struct HomeScreenRoomCell: View { } } label: { HStack(spacing: 16.0) { - if let avatar = room.avatar { - Image(uiImage: avatar) - .resizable() - .scaledToFill() - .frame(width: avatarSize, height: avatarSize) - .clipShape(Circle()) - .accessibilityHidden(true) - } else { - PlaceholderAvatarImage(text: room.name, contentId: room.roomId) - .clipShape(Circle()) - .frame(width: avatarSize, height: avatarSize) - .accessibilityHidden(true) - } + avatar - HStack(alignment: .top) { - VStack(alignment: .leading, spacing: 2.0) { - Text(room.name) - .font(.element.callout.bold()) - .foregroundColor(.element.primaryContent) - .lineLimit(1) - - if let lastMessage = room.lastMessage, !String(lastMessage.characters).isEmpty { - Text(lastMessage) - .font(lastMessageFont) - .foregroundColor(lastMessageForegroundColor) - .lineLimit(2) - .multilineTextAlignment(.leading) - .padding(.top, 2) - .id(lastMessage) - .transition(.opacity.animation(.elementDefault)) - } - } - .animation(.elementDefault, value: room) - - Spacer() - - VStack(alignment: .trailing, spacing: 3.0) { - if let timestamp = room.timestamp { - Text(timestamp) - .font(.element.caption1) - .foregroundColor(.element.secondaryContent) - .id(timestamp) - .transition(.opacity.animation(.elementDefault)) - } - - if room.hasUnreads { - Rectangle() - .frame(width: 12, height: 12) - .foregroundColor(.element.primaryContent) - .clipShape(Circle()) - .transition(.opacity.animation(.elementDefault)) - } - } - .animation(.elementDefault, value: room) + VStack(alignment: .leading, spacing: 2) { + header + footer } } .frame(minHeight: 64.0) @@ -93,23 +44,98 @@ struct HomeScreenRoomCell: View { } } } + .buttonStyle(HomeScreenRoomCellButtonStyle()) .accessibilityIdentifier("roomName:\(room.name)") } - var lastMessageFont: Font { - if room.hasUnreads { - return .element.subheadline.bold() + @ViewBuilder + var avatar: some View { + if let avatar = room.avatar { + Image(uiImage: avatar) + .resizable() + .scaledToFill() + .frame(width: avatarSize, height: avatarSize) + .clipShape(Circle()) + .accessibilityHidden(true) } else { - return .element.subheadline + PlaceholderAvatarImage(text: room.name, contentId: room.roomId) + .clipShape(Circle()) + .frame(width: avatarSize, height: avatarSize) + .accessibilityHidden(true) } } - var lastMessageForegroundColor: Color { - if room.hasUnreads { - return .element.primaryContent - } else { - return .element.secondaryContent + @ViewBuilder + var header: some View { + HStack(alignment: .firstTextBaseline) { + Text(room.name) + .font(.element.callout.bold()) + .foregroundColor(.element.primaryContent) + .lineLimit(1) + .frame(maxWidth: .infinity, alignment: .leading) + + if let timestamp = room.timestamp { + Text(timestamp) + .font(.element.caption1) + .foregroundColor(.element.secondaryContent) + .id(timestamp) + .transition(.opacity.animation(.elementDefault)) + } + } + .animation(.elementDefault, value: room) + } + + @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() + + if let lastMessage = room.lastMessage, !String(lastMessage.characters).isEmpty { + Text(lastMessage) + .lastMessageFormatting() + .id(lastMessage) + .transition(.opacity.animation(.elementDefault)) + } + } + + Spacer() + + if room.hasUnreads { + Rectangle() + .frame(width: 12, height: 12) + .foregroundColor(.element.primaryContent) + .clipShape(Circle()) + .transition(.opacity.animation(.elementDefault)) + } } + .animation(.elementDefault, value: room) + } +} + +struct HomeScreenRoomCellButtonStyle: ButtonStyle { + func makeBody(configuration: Configuration) -> some View { + configuration.label + .roomCellBackground(configuration.isPressed ? .element.system : .element.background) + } +} + +private extension View { + func lastMessageFormatting() -> some View { + font(.element.subheadline) + .foregroundColor(.element.secondaryContent) + .lineLimit(2) + .multilineTextAlignment(.leading) + .padding(.top, 2) + } + + // To be used to indicate the selected room too + func roomCellBackground(_ background: Color) -> some View { + padding(.horizontal, 8) + .padding(.vertical, 6) + .background { background.clipShape(RoundedRectangle(cornerRadius: 12)) } + .padding(.horizontal, 8) } } @@ -136,11 +162,12 @@ struct HomeScreenRoomCell_Previews: PreviewProvider { roomId: details.id, name: details.name, hasUnreads: details.unreadNotificationCount > 0, - timestamp: Date.now.formatted(date: .omitted, time: .shortened)) + timestamp: Date.now.formattedMinimal(), + lastMessage: details.lastMessage) } } - return VStack { + return VStack(spacing: 0) { ForEach(rooms) { room in HomeScreenRoomCell(room: room, context: viewModel.context) } From 8b1873fab5ebd1c0c8c49879d136990287d2f612 Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 24 Jan 2023 10:20:50 +0000 Subject: [PATCH 3/4] Fix background. --- .../Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift index 05ddf7a483..69d4910182 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift @@ -117,7 +117,8 @@ struct HomeScreenRoomCell: View { struct HomeScreenRoomCellButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label - .roomCellBackground(configuration.isPressed ? .element.system : .element.background) + .roomCellBackground(configuration.isPressed ? .element.system : .clear) + .contentShape(Rectangle()) } } From 772d4640965d0fe896e21c4c8558f3a1702d62e4 Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 24 Jan 2023 10:24:13 +0000 Subject: [PATCH 4/4] Changelog --- changelog.d/pr-484.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/pr-484.bugfix diff --git a/changelog.d/pr-484.bugfix b/changelog.d/pr-484.bugfix new file mode 100644 index 0000000000..7c62ef9548 --- /dev/null +++ b/changelog.d/pr-484.bugfix @@ -0,0 +1 @@ +Show the date instead of the time in the room list when the last message is from yesterday or before. \ No newline at end of file