mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
Monthly media gallery separators (#3601)
* Fix the order of the items in the media grid. * Add media gallery timeline separators. * Change the `SeparatorRoomTimelineItem` to have it expose a Date timestamp instead of a text string.
This commit is contained in:
parent
ee2da536af
commit
0b85964f73
@ -138,6 +138,7 @@
|
||||
"common_creating_room" = "Creating room…";
|
||||
"common_current_user_left_room" = "Left room";
|
||||
"common_dark" = "Dark";
|
||||
"common_date_separator_this_month" = "This month";
|
||||
"common_decryption_error" = "Decryption error";
|
||||
"common_developer_options" = "Developer options";
|
||||
"common_device_id" = "Device ID";
|
||||
@ -378,13 +379,17 @@
|
||||
"screen_knock_requests_list_accept_all_alert_description" = "Are you sure you want to accept all requests to join?";
|
||||
"screen_knock_requests_list_accept_all_alert_title" = "Accept all requests";
|
||||
"screen_knock_requests_list_accept_all_button_title" = "Accept all";
|
||||
"screen_knock_requests_list_accept_all_loading_title" = "Accepting all requests to join";
|
||||
"screen_knock_requests_list_accept_loading_title" = "Accepting request to join";
|
||||
"screen_knock_requests_list_ban_alert_confirm_button_title" = "Yes, decline and ban";
|
||||
"screen_knock_requests_list_ban_alert_description" = "Are you sure you want to decline and ban %1$@? This user won’t be able to request access to join this room again.";
|
||||
"screen_knock_requests_list_ban_alert_title" = "Decline and ban from accessing";
|
||||
"screen_knock_requests_list_ban_loading_title" = "Declining and banning access";
|
||||
"screen_knock_requests_list_decline_alert_confirm_button_title" = "Yes, decline";
|
||||
"screen_knock_requests_list_decline_alert_description" = "Are you sure you want to decline %1$@ request to join this room?";
|
||||
"screen_knock_requests_list_decline_alert_title" = "Decline access";
|
||||
"screen_knock_requests_list_decline_and_ban_action_title" = "Decline and ban";
|
||||
"screen_knock_requests_list_decline_loading_title" = "Declining request to join";
|
||||
"screen_knock_requests_list_empty_state_description" = "When somebody will ask to join the room, you’ll be able to see their request here.";
|
||||
"screen_knock_requests_list_empty_state_title" = "No pending request to join";
|
||||
"screen_knock_requests_list_title" = "Requests to join";
|
||||
@ -419,7 +424,22 @@
|
||||
"screen_room_single_knock_request_view_button_title" = "View";
|
||||
"screen_room_details_pinned_events_row_title" = "Pinned messages";
|
||||
"screen_room_details_requests_to_join_title" = "Requests to join";
|
||||
"screen_room_details_security_and_privacy_title" = "Security & privacy";
|
||||
"screen_roomlist_knock_event_sent_description" = "Request to join sent";
|
||||
"screen_security_and_privacy_ask_to_join_option_description" = "Anyone can ask to join the room but an administrator or moderator will have to accept the request.";
|
||||
"screen_security_and_privacy_ask_to_join_option_title" = "Ask to join";
|
||||
"screen_security_and_privacy_enable_encryption_alert_confirm_button_title" = "Yes, enable encryption";
|
||||
"screen_security_and_privacy_enable_encryption_alert_description" = "Once enabled, encryption for a room cannot be disabled, Message history will only be visible for room members since they were invited or since they joined the room.\nNo one besides the room members will be able to read messages. This may prevent bots and bridges to work correctly.\nWe do not recommend enabling encryption for rooms that anyone can find and join.";
|
||||
"screen_security_and_privacy_enable_encryption_alert_title" = "Enable encryption?";
|
||||
"screen_security_and_privacy_encryption_section_footer" = "Once enabled, encryption cannot be disabled.";
|
||||
"screen_security_and_privacy_encryption_section_title" = "Encryption";
|
||||
"screen_security_and_privacy_encryption_toggle_title" = "Enable end-to-end encryption";
|
||||
"screen_security_and_privacy_room_access_anyone_option_description" = "Anyone can find and join";
|
||||
"screen_security_and_privacy_room_access_anyone_option_title" = "Anyone";
|
||||
"screen_security_and_privacy_room_access_invite_only_option_description" = "People can only join if they are invited";
|
||||
"screen_security_and_privacy_room_access_invite_only_option_title" = "Invite only";
|
||||
"screen_security_and_privacy_room_access_section_title" = "Room access";
|
||||
"screen_security_and_privacy_title" = "Security & privacy";
|
||||
"screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed.";
|
||||
"screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices.";
|
||||
"screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices.";
|
||||
@ -1004,6 +1024,7 @@
|
||||
"test_language_identifier" = "en";
|
||||
"test_untranslated_default_language_identifier" = "en";
|
||||
"timeline_decryption_failure_historical_event_no_key_backup" = "Historical messages are not available on this device";
|
||||
"timeline_decryption_failure_historical_event_user_not_joined" = "You don't have access to this message";
|
||||
"timeline_decryption_failure_unable_to_decrypt" = "Unable to decrypt message";
|
||||
"timeline_decryption_failure_withheld_unverified" = "This message was blocked either because you did not verify your device or because the sender needs to verify your identity.";
|
||||
"troubleshoot_notifications_entry_point_section" = "Troubleshoot";
|
||||
|
@ -314,6 +314,8 @@ internal enum L10n {
|
||||
internal static var commonCurrentUserLeftRoom: String { return L10n.tr("Localizable", "common_current_user_left_room") }
|
||||
/// Dark
|
||||
internal static var commonDark: String { return L10n.tr("Localizable", "common_dark") }
|
||||
/// This month
|
||||
internal static var commonDateSeparatorThisMonth: String { return L10n.tr("Localizable", "common_date_separator_this_month") }
|
||||
/// Decryption error
|
||||
internal static var commonDecryptionError: String { return L10n.tr("Localizable", "common_decryption_error") }
|
||||
/// Developer options
|
||||
@ -1314,6 +1316,10 @@ internal enum L10n {
|
||||
internal static var screenKnockRequestsListAcceptAllAlertTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_accept_all_alert_title") }
|
||||
/// Accept all
|
||||
internal static var screenKnockRequestsListAcceptAllButtonTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_accept_all_button_title") }
|
||||
/// Accepting all requests to join
|
||||
internal static var screenKnockRequestsListAcceptAllLoadingTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_accept_all_loading_title") }
|
||||
/// Accepting request to join
|
||||
internal static var screenKnockRequestsListAcceptLoadingTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_accept_loading_title") }
|
||||
/// Yes, decline and ban
|
||||
internal static var screenKnockRequestsListBanAlertConfirmButtonTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_ban_alert_confirm_button_title") }
|
||||
/// Are you sure you want to decline and ban %1$@? This user won’t be able to request access to join this room again.
|
||||
@ -1322,6 +1328,8 @@ internal enum L10n {
|
||||
}
|
||||
/// Decline and ban from accessing
|
||||
internal static var screenKnockRequestsListBanAlertTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_ban_alert_title") }
|
||||
/// Declining and banning access
|
||||
internal static var screenKnockRequestsListBanLoadingTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_ban_loading_title") }
|
||||
/// Yes, decline
|
||||
internal static var screenKnockRequestsListDeclineAlertConfirmButtonTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_decline_alert_confirm_button_title") }
|
||||
/// Are you sure you want to decline %1$@ request to join this room?
|
||||
@ -1332,6 +1340,8 @@ internal enum L10n {
|
||||
internal static var screenKnockRequestsListDeclineAlertTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_decline_alert_title") }
|
||||
/// Decline and ban
|
||||
internal static var screenKnockRequestsListDeclineAndBanActionTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_decline_and_ban_action_title") }
|
||||
/// Declining request to join
|
||||
internal static var screenKnockRequestsListDeclineLoadingTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_decline_loading_title") }
|
||||
/// When somebody will ask to join the room, you’ll be able to see their request here.
|
||||
internal static var screenKnockRequestsListEmptyStateDescription: String { return L10n.tr("Localizable", "screen_knock_requests_list_empty_state_description") }
|
||||
/// No pending request to join
|
||||
@ -1826,6 +1836,8 @@ internal enum L10n {
|
||||
internal static var screenRoomDetailsRolesAndPermissions: String { return L10n.tr("Localizable", "screen_room_details_roles_and_permissions") }
|
||||
/// Room name
|
||||
internal static var screenRoomDetailsRoomNameLabel: String { return L10n.tr("Localizable", "screen_room_details_room_name_label") }
|
||||
/// Security & privacy
|
||||
internal static var screenRoomDetailsSecurityAndPrivacyTitle: String { return L10n.tr("Localizable", "screen_room_details_security_and_privacy_title") }
|
||||
/// Security
|
||||
internal static var screenRoomDetailsSecurityTitle: String { return L10n.tr("Localizable", "screen_room_details_security_title") }
|
||||
/// Share room
|
||||
@ -2118,6 +2130,36 @@ internal enum L10n {
|
||||
internal static var screenRoomlistMarkAsUnread: String { return L10n.tr("Localizable", "screen_roomlist_mark_as_unread") }
|
||||
/// Browse all rooms
|
||||
internal static var screenRoomlistRoomDirectoryButtonTitle: String { return L10n.tr("Localizable", "screen_roomlist_room_directory_button_title") }
|
||||
/// Anyone can ask to join the room but an administrator or moderator will have to accept the request.
|
||||
internal static var screenSecurityAndPrivacyAskToJoinOptionDescription: String { return L10n.tr("Localizable", "screen_security_and_privacy_ask_to_join_option_description") }
|
||||
/// Ask to join
|
||||
internal static var screenSecurityAndPrivacyAskToJoinOptionTitle: String { return L10n.tr("Localizable", "screen_security_and_privacy_ask_to_join_option_title") }
|
||||
/// Yes, enable encryption
|
||||
internal static var screenSecurityAndPrivacyEnableEncryptionAlertConfirmButtonTitle: String { return L10n.tr("Localizable", "screen_security_and_privacy_enable_encryption_alert_confirm_button_title") }
|
||||
/// Once enabled, encryption for a room cannot be disabled, Message history will only be visible for room members since they were invited or since they joined the room.
|
||||
/// No one besides the room members will be able to read messages. This may prevent bots and bridges to work correctly.
|
||||
/// We do not recommend enabling encryption for rooms that anyone can find and join.
|
||||
internal static var screenSecurityAndPrivacyEnableEncryptionAlertDescription: String { return L10n.tr("Localizable", "screen_security_and_privacy_enable_encryption_alert_description") }
|
||||
/// Enable encryption?
|
||||
internal static var screenSecurityAndPrivacyEnableEncryptionAlertTitle: String { return L10n.tr("Localizable", "screen_security_and_privacy_enable_encryption_alert_title") }
|
||||
/// Once enabled, encryption cannot be disabled.
|
||||
internal static var screenSecurityAndPrivacyEncryptionSectionFooter: String { return L10n.tr("Localizable", "screen_security_and_privacy_encryption_section_footer") }
|
||||
/// Encryption
|
||||
internal static var screenSecurityAndPrivacyEncryptionSectionTitle: String { return L10n.tr("Localizable", "screen_security_and_privacy_encryption_section_title") }
|
||||
/// Enable end-to-end encryption
|
||||
internal static var screenSecurityAndPrivacyEncryptionToggleTitle: String { return L10n.tr("Localizable", "screen_security_and_privacy_encryption_toggle_title") }
|
||||
/// Anyone can find and join
|
||||
internal static var screenSecurityAndPrivacyRoomAccessAnyoneOptionDescription: String { return L10n.tr("Localizable", "screen_security_and_privacy_room_access_anyone_option_description") }
|
||||
/// Anyone
|
||||
internal static var screenSecurityAndPrivacyRoomAccessAnyoneOptionTitle: String { return L10n.tr("Localizable", "screen_security_and_privacy_room_access_anyone_option_title") }
|
||||
/// People can only join if they are invited
|
||||
internal static var screenSecurityAndPrivacyRoomAccessInviteOnlyOptionDescription: String { return L10n.tr("Localizable", "screen_security_and_privacy_room_access_invite_only_option_description") }
|
||||
/// Invite only
|
||||
internal static var screenSecurityAndPrivacyRoomAccessInviteOnlyOptionTitle: String { return L10n.tr("Localizable", "screen_security_and_privacy_room_access_invite_only_option_title") }
|
||||
/// Room access
|
||||
internal static var screenSecurityAndPrivacyRoomAccessSectionTitle: String { return L10n.tr("Localizable", "screen_security_and_privacy_room_access_section_title") }
|
||||
/// Security & privacy
|
||||
internal static var screenSecurityAndPrivacyTitle: String { return L10n.tr("Localizable", "screen_security_and_privacy_title") }
|
||||
/// Change account provider
|
||||
internal static var screenServerConfirmationChangeServer: String { return L10n.tr("Localizable", "screen_server_confirmation_change_server") }
|
||||
/// A private server for Element employees.
|
||||
@ -2526,6 +2568,8 @@ internal enum L10n {
|
||||
internal static var testUntranslatedDefaultLanguageIdentifier: String { return L10n.tr("Localizable", "test_untranslated_default_language_identifier") }
|
||||
/// Historical messages are not available on this device
|
||||
internal static var timelineDecryptionFailureHistoricalEventNoKeyBackup: String { return L10n.tr("Localizable", "timeline_decryption_failure_historical_event_no_key_backup") }
|
||||
/// You don't have access to this message
|
||||
internal static var timelineDecryptionFailureHistoricalEventUserNotJoined: String { return L10n.tr("Localizable", "timeline_decryption_failure_historical_event_user_not_joined") }
|
||||
/// Unable to decrypt message
|
||||
internal static var timelineDecryptionFailureUnableToDecrypt: String { return L10n.tr("Localizable", "timeline_decryption_failure_unable_to_decrypt") }
|
||||
/// This message was blocked either because you did not verify your device or because the sender needs to verify your identity.
|
||||
|
@ -14,9 +14,15 @@ enum MediaEventsTimelineScreenMode {
|
||||
case files
|
||||
}
|
||||
|
||||
struct MediaEventsTimelineGroup: Identifiable {
|
||||
var id: String
|
||||
var title: String
|
||||
var items: [RoomTimelineItemViewState]
|
||||
}
|
||||
|
||||
struct MediaEventsTimelineScreenViewState: BindableState {
|
||||
var isBackPaginating = false
|
||||
var items = [RoomTimelineItemViewState]()
|
||||
var groups = [MediaEventsTimelineGroup]()
|
||||
|
||||
var bindings: MediaEventsTimelineScreenViewStateBindings
|
||||
}
|
||||
|
@ -84,16 +84,41 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType
|
||||
// MARK: - Private
|
||||
|
||||
private func updateWithTimelineViewState(_ timelineViewState: TimelineViewState) {
|
||||
state.items = timelineViewState.timelineState.itemViewStates.filter { itemViewState in
|
||||
var newGroups = [MediaEventsTimelineGroup]()
|
||||
var currentItems = [RoomTimelineItemViewState]()
|
||||
|
||||
timelineViewState.timelineState.itemViewStates.filter { itemViewState in
|
||||
switch itemViewState.type {
|
||||
case .image, .video:
|
||||
state.bindings.screenMode == .media
|
||||
case .audio, .file:
|
||||
case .audio, .file, .voice:
|
||||
state.bindings.screenMode == .files
|
||||
case .separator:
|
||||
true
|
||||
default:
|
||||
false
|
||||
}
|
||||
}.reversed()
|
||||
}.reversed().forEach { item in
|
||||
if case .separator(let item) = item.type {
|
||||
let group = MediaEventsTimelineGroup(id: item.id.uniqueID.id,
|
||||
title: titleForDate(item.timestamp),
|
||||
items: currentItems)
|
||||
currentItems = []
|
||||
newGroups.append(group)
|
||||
} else {
|
||||
currentItems.append(item)
|
||||
}
|
||||
}
|
||||
|
||||
if !currentItems.isEmpty {
|
||||
MXLog.warning("Found ungrouped timeline items, appending them at end.")
|
||||
let group = MediaEventsTimelineGroup(id: UUID().uuidString,
|
||||
title: titleForDate(.now),
|
||||
items: currentItems)
|
||||
newGroups.append(group)
|
||||
}
|
||||
|
||||
state.groups = newGroups
|
||||
|
||||
state.isBackPaginating = (timelineViewState.timelineState.paginationState.backward == .paginating)
|
||||
backPaginateIfNecessary(paginationStatus: timelineViewState.timelineState.paginationState.backward)
|
||||
@ -124,4 +149,12 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType
|
||||
userIndicatorController: userIndicatorController)
|
||||
state.bindings.mediaPreviewViewModel = viewModel
|
||||
}
|
||||
|
||||
private func titleForDate(_ date: Date) -> String {
|
||||
if Calendar.current.isDate(date, equalTo: .now, toGranularity: .month) {
|
||||
L10n.commonDateSeparatorThisMonth
|
||||
} else {
|
||||
date.formatted(.dateTime.month(.wide).year())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,13 +33,21 @@ struct MediaEventsTimelineScreen: View {
|
||||
.timelineMediaQuickLook(viewModel: $context.mediaPreviewViewModel)
|
||||
}
|
||||
|
||||
// The scale effects do the following:
|
||||
// * flip the scrollView vertically to keep the items
|
||||
// at the bottom and have pagination working properly
|
||||
// * flip the grid vertically to counteract the scroll view
|
||||
// but also horizontally to preserve the corect item order
|
||||
// * flip the items on both axes have them render correctly
|
||||
@ViewBuilder
|
||||
private var content: some View {
|
||||
ScrollView {
|
||||
Group {
|
||||
let columns = [GridItem(.adaptive(minimum: 80, maximum: 150), spacing: 1)]
|
||||
LazyVGrid(columns: columns, alignment: .center, spacing: 1) {
|
||||
ForEach(context.viewState.items) { item in
|
||||
ForEach(context.viewState.groups) { group in
|
||||
Section(footer: sectionFooterForGroup(group)) {
|
||||
ForEach(group.items) { item in
|
||||
Button {
|
||||
context.send(viewAction: .tappedItem(item))
|
||||
} label: {
|
||||
@ -49,28 +57,15 @@ struct MediaEventsTimelineScreen: View {
|
||||
viewForTimelineItem(item)
|
||||
}
|
||||
.clipped()
|
||||
.scaleEffect(.init(width: 1, height: -1))
|
||||
.scaleEffect(.init(width: -1, height: -1))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.scaleEffect(.init(width: -1, height: 1))
|
||||
|
||||
// Needs to be wrapped in a LazyStack otherwise appearance calls don't trigger
|
||||
LazyVStack(spacing: 0) {
|
||||
Rectangle()
|
||||
.frame(height: 44)
|
||||
.foregroundStyle(.compound.bgCanvasDefault)
|
||||
.overlay {
|
||||
if context.viewState.isBackPaginating {
|
||||
ProgressView()
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
context.send(viewAction: .oldestItemDidAppear)
|
||||
}
|
||||
.onDisappear {
|
||||
context.send(viewAction: .oldestItemDidDisappear)
|
||||
}
|
||||
}
|
||||
header
|
||||
}
|
||||
}
|
||||
.scaleEffect(.init(width: 1, height: -1))
|
||||
@ -79,7 +74,37 @@ struct MediaEventsTimelineScreen: View {
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder func viewForTimelineItem(_ item: RoomTimelineItemViewState) -> some View {
|
||||
private var header: some View {
|
||||
// Needs to be wrapped in a LazyStack otherwise appearance calls don't trigger
|
||||
LazyVStack(spacing: 0) {
|
||||
ProgressView()
|
||||
.padding()
|
||||
.opacity(context.viewState.isBackPaginating ? 1 : 0)
|
||||
|
||||
Rectangle()
|
||||
.frame(height: 1)
|
||||
.foregroundStyle(.compound.bgCanvasDefault)
|
||||
.onAppear {
|
||||
context.send(viewAction: .oldestItemDidAppear)
|
||||
}
|
||||
.onDisappear {
|
||||
context.send(viewAction: .oldestItemDidDisappear)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func sectionFooterForGroup(_ group: MediaEventsTimelineGroup) -> some View {
|
||||
Text(group.title)
|
||||
.font(.compound.bodySMSemibold)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
.frame(alignment: .center)
|
||||
.scaleEffect(.init(width: -1, height: -1))
|
||||
.padding(.vertical, 16)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func viewForTimelineItem(_ item: RoomTimelineItemViewState) -> some View {
|
||||
switch item.type {
|
||||
case .image(let timelineItem):
|
||||
#warning("Make this work for gifs")
|
||||
@ -107,8 +132,6 @@ struct MediaEventsTimelineScreen: View {
|
||||
} else {
|
||||
playIcon
|
||||
}
|
||||
case .separator(let timelineItem):
|
||||
Text(timelineItem.text)
|
||||
default:
|
||||
EmptyView()
|
||||
}
|
||||
@ -169,12 +192,12 @@ struct MediaEventsTimelineScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
MediaEventsTimelineScreen(context: mediaViewModel.context)
|
||||
.previewDisplayName("Media")
|
||||
}
|
||||
.previewDisplayName("Media")
|
||||
|
||||
NavigationStack {
|
||||
MediaEventsTimelineScreen(context: filesViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Files")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,8 +52,8 @@ struct CollapsibleRoomTimelineView: View {
|
||||
|
||||
struct CollapsibleRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
||||
static let item = CollapsibleTimelineItem(items: [
|
||||
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "First separator")), text: "This is a separator"),
|
||||
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Second separator")), text: "This is another separator")
|
||||
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "First separator")), timestamp: .mock),
|
||||
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Second separator")), timestamp: .mock)
|
||||
])
|
||||
|
||||
static var previews: some View {
|
||||
|
@ -33,7 +33,7 @@ struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
||||
|
||||
static var previews: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: .init(id: "Separator")), text: "Today")), groupStyle: .single))
|
||||
RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: .init(id: "Separator")), timestamp: .mock)), groupStyle: .single))
|
||||
RoomTimelineItemView(viewState: .init(type: .text(.init(id: .randomEvent,
|
||||
timestamp: .mock,
|
||||
isOutgoing: true,
|
||||
@ -45,7 +45,7 @@ struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
||||
|
||||
ReadMarkerRoomTimelineView(timelineItem: item)
|
||||
|
||||
RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: .init(id: "Separator")), text: "Today")), groupStyle: .single))
|
||||
RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: .init(id: "Separator")), timestamp: .mock)), groupStyle: .single))
|
||||
RoomTimelineItemView(viewState: .init(type: .text(.init(id: .randomEvent,
|
||||
timestamp: .mock,
|
||||
isOutgoing: false,
|
||||
|
@ -11,7 +11,7 @@ struct SeparatorRoomTimelineView: View {
|
||||
let timelineItem: SeparatorRoomTimelineItem
|
||||
|
||||
var body: some View {
|
||||
Text(timelineItem.text)
|
||||
Text(timelineItem.timestamp.formatted(date: .complete, time: .omitted))
|
||||
.font(.compound.bodySMSemibold)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
.frame(maxWidth: .infinity)
|
||||
@ -24,7 +24,7 @@ struct SeparatorRoomTimelineView: View {
|
||||
struct SeparatorRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
||||
static var previews: some View {
|
||||
let item = SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Separator")),
|
||||
text: "This is a separator")
|
||||
timestamp: .mock)
|
||||
SeparatorRoomTimelineView(timelineItem: item)
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import Foundation
|
||||
enum RoomTimelineItemFixtures {
|
||||
/// The default timeline items used in Xcode previews etc.
|
||||
static var `default`: [RoomTimelineItemProtocol] = [
|
||||
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Yesterday")), text: "Yesterday"),
|
||||
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Yesterday")), timestamp: .mock),
|
||||
TextRoomTimelineItem(id: .event(uniqueID: .init(id: ".RoomTimelineItemFixtures.default.0"),
|
||||
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.0")),
|
||||
timestamp: .mock,
|
||||
@ -52,7 +52,7 @@ enum RoomTimelineItemFixtures {
|
||||
ReactionSender(id: "jacob", timestamp: Date())
|
||||
])
|
||||
])),
|
||||
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Today")), text: "Today"),
|
||||
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Today")), timestamp: .mock),
|
||||
TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.3"),
|
||||
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.3")),
|
||||
timestamp: .mock,
|
||||
@ -262,6 +262,11 @@ enum RoomTimelineItemFixtures {
|
||||
VoiceMessageRoomTimelineItem(isOutgoing: true)
|
||||
]
|
||||
}
|
||||
|
||||
static var separator: SeparatorRoomTimelineItem {
|
||||
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: UUID().uuidString)),
|
||||
timestamp: .now)
|
||||
}
|
||||
}
|
||||
|
||||
private extension TextRoomTimelineItem {
|
||||
|
@ -42,7 +42,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
switch timelineKind {
|
||||
case .media:
|
||||
timelineItems = (0..<5).reduce([]) { partialResult, _ in
|
||||
partialResult + RoomTimelineItemFixtures.mediaChunk
|
||||
partialResult + [RoomTimelineItemFixtures.separator] + RoomTimelineItemFixtures.mediaChunk
|
||||
}
|
||||
default:
|
||||
break
|
||||
|
@ -472,9 +472,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
switch virtualItem {
|
||||
case .dateDivider(let timestamp):
|
||||
let date = Date(timeIntervalSince1970: TimeInterval(timestamp / 1000))
|
||||
let dateString = date.formatted(date: .complete, time: .omitted)
|
||||
|
||||
return SeparatorRoomTimelineItem(id: .virtual(uniqueID: uniqueID), text: dateString)
|
||||
return SeparatorRoomTimelineItem(id: .virtual(uniqueID: uniqueID), timestamp: date)
|
||||
case .readMarker:
|
||||
return ReadMarkerRoomTimelineItem(id: .virtual(uniqueID: uniqueID))
|
||||
}
|
||||
|
@ -9,5 +9,5 @@ import Foundation
|
||||
|
||||
struct SeparatorRoomTimelineItem: DecorationTimelineItemProtocol, Equatable {
|
||||
let id: TimelineItemIdentifier
|
||||
let text: String
|
||||
let timestamp: Date
|
||||
}
|
||||
|
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.2.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.2.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.Files.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.Files.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.Media.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.Media.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.2.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.2.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.Files.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.Files.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.Media.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.Media.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.2.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.2.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.Files.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.Files.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.Media.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.Media.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-pseudo.2.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-pseudo.2.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-pseudo.Files.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-pseudo.Files.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-pseudo.Media.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-pseudo.Media.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_readMarkerRoomTimelineView-iPad-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_readMarkerRoomTimelineView-iPad-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_readMarkerRoomTimelineView-iPad-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_readMarkerRoomTimelineView-iPad-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_readMarkerRoomTimelineView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_readMarkerRoomTimelineView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_readMarkerRoomTimelineView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_readMarkerRoomTimelineView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreen-iPad-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreen-iPad-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreen-iPad-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreen-iPad-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreen-iPhone-16-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreen-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreen-iPhone-16-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreen-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorRoomTimelineView-iPad-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorRoomTimelineView-iPad-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorRoomTimelineView-iPad-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorRoomTimelineView-iPad-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorRoomTimelineView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorRoomTimelineView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorRoomTimelineView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorRoomTimelineView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineView-iPad-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineView-iPad-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineView-iPad-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineView-iPad-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/roomPlainNoAvatar-iPad-18-1-en-GB.UI.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/roomPlainNoAvatar-iPad-18-1-en-GB.UI.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/roomPlainNoAvatar-iPhone-18-1-en-GB.UI.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/roomPlainNoAvatar-iPhone-18-1-en-GB.UI.png
(Stored with Git LFS)
Binary file not shown.
@ -441,7 +441,7 @@ private extension TextRoomTimelineItem {
|
||||
|
||||
private extension SeparatorRoomTimelineItem {
|
||||
init(uniqueID: TimelineUniqueId) {
|
||||
self.init(id: .virtual(uniqueID: uniqueID), text: "")
|
||||
self.init(id: .virtual(uniqueID: uniqueID), timestamp: .mock)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user