Show the date on a room when the last message is older than today. (#484)
* Format the last message date correctly. * Update room cell layout - fixes an issue where the longer the date got the smaller the last message width was.
This commit is contained in:
@@ -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 = "<group>"; };
|
||||
2069C264213B9F381DF9F876 /* ta */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ta; path = ta.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
2112A6CFEA46E672D90EBF54 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
2141693488CE5446BB391964 /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = "<group>"; };
|
||||
218AB05B4E3889731959C5F1 /* EventBasedTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBasedTimelineItemProtocol.swift; sourceTree = "<group>"; };
|
||||
21BA866267F84BF4350B0CB7 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-BR"; path = "pt-BR.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||
227AC5D71A4CE43512062243 /* URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = "<group>"; };
|
||||
@@ -657,6 +662,7 @@
|
||||
39EBB6903EFD4236B8D11A42 /* fr-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "fr-CA"; path = "fr-CA.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||
3ACBDC1D28EFB7789EB467E0 /* MockRoomProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomProxy.swift; sourceTree = "<group>"; };
|
||||
3B5B535DA49C54523FF7A412 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
3B5E97E9615A158C76B2AB77 /* DateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTests.swift; sourceTree = "<group>"; };
|
||||
3BFDAF6918BB096C44788FC9 /* RoomDetailsScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreenUITests.swift; sourceTree = "<group>"; };
|
||||
3C1A3D524D63815B28FA4D62 /* EmojiCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCategory.swift; sourceTree = "<group>"; };
|
||||
3CDF9E55650D6035D6536538 /* nb-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "nb-NO"; path = "nb-NO.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||
@@ -897,6 +903,7 @@
|
||||
AE225C66978648AA4AF37B45 /* te */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = te; path = te.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
AE5DDBEBBA17973ED4638823 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
AEC96B3DC55090BBF8876CC2 /* MockFileCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockFileCache.swift; sourceTree = "<group>"; };
|
||||
AEEAFB646E583655652C3D04 /* RoomStateEventStringBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomStateEventStringBuilderTests.swift; sourceTree = "<group>"; };
|
||||
AF11DD57D9FACF2A757AB024 /* AnalyticsPromptUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptUITests.swift; sourceTree = "<group>"; };
|
||||
AF25E364AE85090A70AE4644 /* AttributedStringBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringBuilderTests.swift; sourceTree = "<group>"; };
|
||||
B07B937B036247F1962BBCC7 /* RoomMemberDetailsMemberCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsMemberCell.swift; sourceTree = "<group>"; };
|
||||
@@ -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 */,
|
||||
|
||||
43
ElementX/Sources/Other/Extensions/Date.swift
Normal file
43
ElementX/Sources/Other/Extensions/Date.swift
Normal file
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,99 @@ 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 : .clear)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
}
|
||||
|
||||
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 +163,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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
43
UnitTests/Sources/DateTests.swift
Normal file
43
UnitTests/Sources/DateTests.swift
Normal file
@@ -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")
|
||||
}
|
||||
}
|
||||
1
changelog.d/pr-484.bugfix
Normal file
1
changelog.d/pr-484.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Show the date instead of the time in the room list when the last message is from yesterday or before.
|
||||
Reference in New Issue
Block a user