mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-07 04:48:34 +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:
@@ -138,6 +138,7 @@
|
|||||||
"common_creating_room" = "Creating room…";
|
"common_creating_room" = "Creating room…";
|
||||||
"common_current_user_left_room" = "Left room";
|
"common_current_user_left_room" = "Left room";
|
||||||
"common_dark" = "Dark";
|
"common_dark" = "Dark";
|
||||||
|
"common_date_separator_this_month" = "This month";
|
||||||
"common_decryption_error" = "Decryption error";
|
"common_decryption_error" = "Decryption error";
|
||||||
"common_developer_options" = "Developer options";
|
"common_developer_options" = "Developer options";
|
||||||
"common_device_id" = "Device ID";
|
"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_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_alert_title" = "Accept all requests";
|
||||||
"screen_knock_requests_list_accept_all_button_title" = "Accept all";
|
"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_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_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_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_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_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_alert_title" = "Decline access";
|
||||||
"screen_knock_requests_list_decline_and_ban_action_title" = "Decline and ban";
|
"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_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_empty_state_title" = "No pending request to join";
|
||||||
"screen_knock_requests_list_title" = "Requests 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_single_knock_request_view_button_title" = "View";
|
||||||
"screen_room_details_pinned_events_row_title" = "Pinned messages";
|
"screen_room_details_pinned_events_row_title" = "Pinned messages";
|
||||||
"screen_room_details_requests_to_join_title" = "Requests to join";
|
"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_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_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_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.";
|
"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_language_identifier" = "en";
|
||||||
"test_untranslated_default_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_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_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.";
|
"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";
|
"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") }
|
internal static var commonCurrentUserLeftRoom: String { return L10n.tr("Localizable", "common_current_user_left_room") }
|
||||||
/// Dark
|
/// Dark
|
||||||
internal static var commonDark: String { return L10n.tr("Localizable", "common_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
|
/// Decryption error
|
||||||
internal static var commonDecryptionError: String { return L10n.tr("Localizable", "common_decryption_error") }
|
internal static var commonDecryptionError: String { return L10n.tr("Localizable", "common_decryption_error") }
|
||||||
/// Developer options
|
/// 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") }
|
internal static var screenKnockRequestsListAcceptAllAlertTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_accept_all_alert_title") }
|
||||||
/// Accept all
|
/// Accept all
|
||||||
internal static var screenKnockRequestsListAcceptAllButtonTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_accept_all_button_title") }
|
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
|
/// Yes, decline and ban
|
||||||
internal static var screenKnockRequestsListBanAlertConfirmButtonTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_ban_alert_confirm_button_title") }
|
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.
|
/// 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
|
/// Decline and ban from accessing
|
||||||
internal static var screenKnockRequestsListBanAlertTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_ban_alert_title") }
|
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
|
/// Yes, decline
|
||||||
internal static var screenKnockRequestsListDeclineAlertConfirmButtonTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_decline_alert_confirm_button_title") }
|
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?
|
/// 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") }
|
internal static var screenKnockRequestsListDeclineAlertTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_decline_alert_title") }
|
||||||
/// Decline and ban
|
/// Decline and ban
|
||||||
internal static var screenKnockRequestsListDeclineAndBanActionTitle: String { return L10n.tr("Localizable", "screen_knock_requests_list_decline_and_ban_action_title") }
|
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.
|
/// 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") }
|
internal static var screenKnockRequestsListEmptyStateDescription: String { return L10n.tr("Localizable", "screen_knock_requests_list_empty_state_description") }
|
||||||
/// No pending request to join
|
/// 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") }
|
internal static var screenRoomDetailsRolesAndPermissions: String { return L10n.tr("Localizable", "screen_room_details_roles_and_permissions") }
|
||||||
/// Room name
|
/// Room name
|
||||||
internal static var screenRoomDetailsRoomNameLabel: String { return L10n.tr("Localizable", "screen_room_details_room_name_label") }
|
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
|
/// Security
|
||||||
internal static var screenRoomDetailsSecurityTitle: String { return L10n.tr("Localizable", "screen_room_details_security_title") }
|
internal static var screenRoomDetailsSecurityTitle: String { return L10n.tr("Localizable", "screen_room_details_security_title") }
|
||||||
/// Share room
|
/// Share room
|
||||||
@@ -2118,6 +2130,36 @@ internal enum L10n {
|
|||||||
internal static var screenRoomlistMarkAsUnread: String { return L10n.tr("Localizable", "screen_roomlist_mark_as_unread") }
|
internal static var screenRoomlistMarkAsUnread: String { return L10n.tr("Localizable", "screen_roomlist_mark_as_unread") }
|
||||||
/// Browse all rooms
|
/// Browse all rooms
|
||||||
internal static var screenRoomlistRoomDirectoryButtonTitle: String { return L10n.tr("Localizable", "screen_roomlist_room_directory_button_title") }
|
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
|
/// Change account provider
|
||||||
internal static var screenServerConfirmationChangeServer: String { return L10n.tr("Localizable", "screen_server_confirmation_change_server") }
|
internal static var screenServerConfirmationChangeServer: String { return L10n.tr("Localizable", "screen_server_confirmation_change_server") }
|
||||||
/// A private server for Element employees.
|
/// 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") }
|
internal static var testUntranslatedDefaultLanguageIdentifier: String { return L10n.tr("Localizable", "test_untranslated_default_language_identifier") }
|
||||||
/// Historical messages are not available on this device
|
/// 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") }
|
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
|
/// Unable to decrypt message
|
||||||
internal static var timelineDecryptionFailureUnableToDecrypt: String { return L10n.tr("Localizable", "timeline_decryption_failure_unable_to_decrypt") }
|
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.
|
/// This message was blocked either because you did not verify your device or because the sender needs to verify your identity.
|
||||||
|
|||||||
+7
-1
@@ -14,9 +14,15 @@ enum MediaEventsTimelineScreenMode {
|
|||||||
case files
|
case files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct MediaEventsTimelineGroup: Identifiable {
|
||||||
|
var id: String
|
||||||
|
var title: String
|
||||||
|
var items: [RoomTimelineItemViewState]
|
||||||
|
}
|
||||||
|
|
||||||
struct MediaEventsTimelineScreenViewState: BindableState {
|
struct MediaEventsTimelineScreenViewState: BindableState {
|
||||||
var isBackPaginating = false
|
var isBackPaginating = false
|
||||||
var items = [RoomTimelineItemViewState]()
|
var groups = [MediaEventsTimelineGroup]()
|
||||||
|
|
||||||
var bindings: MediaEventsTimelineScreenViewStateBindings
|
var bindings: MediaEventsTimelineScreenViewStateBindings
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-3
@@ -84,16 +84,41 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType
|
|||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func updateWithTimelineViewState(_ timelineViewState: TimelineViewState) {
|
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 {
|
switch itemViewState.type {
|
||||||
case .image, .video:
|
case .image, .video:
|
||||||
state.bindings.screenMode == .media
|
state.bindings.screenMode == .media
|
||||||
case .audio, .file:
|
case .audio, .file, .voice:
|
||||||
state.bindings.screenMode == .files
|
state.bindings.screenMode == .files
|
||||||
|
case .separator:
|
||||||
|
true
|
||||||
default:
|
default:
|
||||||
false
|
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)
|
state.isBackPaginating = (timelineViewState.timelineState.paginationState.backward == .paginating)
|
||||||
backPaginateIfNecessary(paginationStatus: timelineViewState.timelineState.paginationState.backward)
|
backPaginateIfNecessary(paginationStatus: timelineViewState.timelineState.paginationState.backward)
|
||||||
@@ -124,4 +149,12 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType
|
|||||||
userIndicatorController: userIndicatorController)
|
userIndicatorController: userIndicatorController)
|
||||||
state.bindings.mediaPreviewViewModel = viewModel
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+55
-32
@@ -33,44 +33,39 @@ struct MediaEventsTimelineScreen: View {
|
|||||||
.timelineMediaQuickLook(viewModel: $context.mediaPreviewViewModel)
|
.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
|
@ViewBuilder
|
||||||
private var content: some View {
|
private var content: some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
Group {
|
Group {
|
||||||
let columns = [GridItem(.adaptive(minimum: 80, maximum: 150), spacing: 1)]
|
let columns = [GridItem(.adaptive(minimum: 80, maximum: 150), spacing: 1)]
|
||||||
LazyVGrid(columns: columns, alignment: .center, spacing: 1) {
|
LazyVGrid(columns: columns, alignment: .center, spacing: 1) {
|
||||||
ForEach(context.viewState.items) { item in
|
ForEach(context.viewState.groups) { group in
|
||||||
Button {
|
Section(footer: sectionFooterForGroup(group)) {
|
||||||
context.send(viewAction: .tappedItem(item))
|
ForEach(group.items) { item in
|
||||||
} label: {
|
Button {
|
||||||
Color.clear // Let the image aspect fill in place
|
context.send(viewAction: .tappedItem(item))
|
||||||
.aspectRatio(1, contentMode: .fill)
|
} label: {
|
||||||
.overlay {
|
Color.clear // Let the image aspect fill in place
|
||||||
viewForTimelineItem(item)
|
.aspectRatio(1, contentMode: .fill)
|
||||||
|
.overlay {
|
||||||
|
viewForTimelineItem(item)
|
||||||
|
}
|
||||||
|
.clipped()
|
||||||
|
.scaleEffect(.init(width: -1, height: -1))
|
||||||
}
|
}
|
||||||
.clipped()
|
}
|
||||||
.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
|
header
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.scaleEffect(.init(width: 1, height: -1))
|
.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 {
|
switch item.type {
|
||||||
case .image(let timelineItem):
|
case .image(let timelineItem):
|
||||||
#warning("Make this work for gifs")
|
#warning("Make this work for gifs")
|
||||||
@@ -107,8 +132,6 @@ struct MediaEventsTimelineScreen: View {
|
|||||||
} else {
|
} else {
|
||||||
playIcon
|
playIcon
|
||||||
}
|
}
|
||||||
case .separator(let timelineItem):
|
|
||||||
Text(timelineItem.text)
|
|
||||||
default:
|
default:
|
||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
@@ -169,12 +192,12 @@ struct MediaEventsTimelineScreen_Previews: PreviewProvider, TestablePreview {
|
|||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
MediaEventsTimelineScreen(context: mediaViewModel.context)
|
MediaEventsTimelineScreen(context: mediaViewModel.context)
|
||||||
.previewDisplayName("Media")
|
|
||||||
}
|
}
|
||||||
|
.previewDisplayName("Media")
|
||||||
|
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
MediaEventsTimelineScreen(context: filesViewModel.context)
|
MediaEventsTimelineScreen(context: filesViewModel.context)
|
||||||
.previewDisplayName("Files")
|
|
||||||
}
|
}
|
||||||
|
.previewDisplayName("Files")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -52,8 +52,8 @@ struct CollapsibleRoomTimelineView: View {
|
|||||||
|
|
||||||
struct CollapsibleRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
struct CollapsibleRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
||||||
static let item = CollapsibleTimelineItem(items: [
|
static let item = CollapsibleTimelineItem(items: [
|
||||||
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "First separator")), text: "This is a separator"),
|
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "First separator")), timestamp: .mock),
|
||||||
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Second separator")), text: "This is another separator")
|
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Second separator")), timestamp: .mock)
|
||||||
])
|
])
|
||||||
|
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
|
|||||||
+2
-2
@@ -33,7 +33,7 @@ struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
|||||||
|
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
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,
|
RoomTimelineItemView(viewState: .init(type: .text(.init(id: .randomEvent,
|
||||||
timestamp: .mock,
|
timestamp: .mock,
|
||||||
isOutgoing: true,
|
isOutgoing: true,
|
||||||
@@ -45,7 +45,7 @@ struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
|||||||
|
|
||||||
ReadMarkerRoomTimelineView(timelineItem: item)
|
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,
|
RoomTimelineItemView(viewState: .init(type: .text(.init(id: .randomEvent,
|
||||||
timestamp: .mock,
|
timestamp: .mock,
|
||||||
isOutgoing: false,
|
isOutgoing: false,
|
||||||
|
|||||||
+2
-2
@@ -11,7 +11,7 @@ struct SeparatorRoomTimelineView: View {
|
|||||||
let timelineItem: SeparatorRoomTimelineItem
|
let timelineItem: SeparatorRoomTimelineItem
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Text(timelineItem.text)
|
Text(timelineItem.timestamp.formatted(date: .complete, time: .omitted))
|
||||||
.font(.compound.bodySMSemibold)
|
.font(.compound.bodySMSemibold)
|
||||||
.foregroundColor(.compound.textPrimary)
|
.foregroundColor(.compound.textPrimary)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
@@ -24,7 +24,7 @@ struct SeparatorRoomTimelineView: View {
|
|||||||
struct SeparatorRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
struct SeparatorRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let item = SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Separator")),
|
let item = SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Separator")),
|
||||||
text: "This is a separator")
|
timestamp: .mock)
|
||||||
SeparatorRoomTimelineView(timelineItem: item)
|
SeparatorRoomTimelineView(timelineItem: item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import Foundation
|
|||||||
enum RoomTimelineItemFixtures {
|
enum RoomTimelineItemFixtures {
|
||||||
/// The default timeline items used in Xcode previews etc.
|
/// The default timeline items used in Xcode previews etc.
|
||||||
static var `default`: [RoomTimelineItemProtocol] = [
|
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"),
|
TextRoomTimelineItem(id: .event(uniqueID: .init(id: ".RoomTimelineItemFixtures.default.0"),
|
||||||
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.0")),
|
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.0")),
|
||||||
timestamp: .mock,
|
timestamp: .mock,
|
||||||
@@ -52,7 +52,7 @@ enum RoomTimelineItemFixtures {
|
|||||||
ReactionSender(id: "jacob", timestamp: Date())
|
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"),
|
TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.3"),
|
||||||
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.3")),
|
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.3")),
|
||||||
timestamp: .mock,
|
timestamp: .mock,
|
||||||
@@ -262,6 +262,11 @@ enum RoomTimelineItemFixtures {
|
|||||||
VoiceMessageRoomTimelineItem(isOutgoing: true)
|
VoiceMessageRoomTimelineItem(isOutgoing: true)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static var separator: SeparatorRoomTimelineItem {
|
||||||
|
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: UUID().uuidString)),
|
||||||
|
timestamp: .now)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension TextRoomTimelineItem {
|
private extension TextRoomTimelineItem {
|
||||||
|
|||||||
+1
-1
@@ -42,7 +42,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
|||||||
switch timelineKind {
|
switch timelineKind {
|
||||||
case .media:
|
case .media:
|
||||||
timelineItems = (0..<5).reduce([]) { partialResult, _ in
|
timelineItems = (0..<5).reduce([]) { partialResult, _ in
|
||||||
partialResult + RoomTimelineItemFixtures.mediaChunk
|
partialResult + [RoomTimelineItemFixtures.separator] + RoomTimelineItemFixtures.mediaChunk
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -472,9 +472,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
|||||||
switch virtualItem {
|
switch virtualItem {
|
||||||
case .dateDivider(let timestamp):
|
case .dateDivider(let timestamp):
|
||||||
let date = Date(timeIntervalSince1970: TimeInterval(timestamp / 1000))
|
let date = Date(timeIntervalSince1970: TimeInterval(timestamp / 1000))
|
||||||
let dateString = date.formatted(date: .complete, time: .omitted)
|
return SeparatorRoomTimelineItem(id: .virtual(uniqueID: uniqueID), timestamp: date)
|
||||||
|
|
||||||
return SeparatorRoomTimelineItem(id: .virtual(uniqueID: uniqueID), text: dateString)
|
|
||||||
case .readMarker:
|
case .readMarker:
|
||||||
return ReadMarkerRoomTimelineItem(id: .virtual(uniqueID: uniqueID))
|
return ReadMarkerRoomTimelineItem(id: .virtual(uniqueID: uniqueID))
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -9,5 +9,5 @@ import Foundation
|
|||||||
|
|
||||||
struct SeparatorRoomTimelineItem: DecorationTimelineItemProtocol, Equatable {
|
struct SeparatorRoomTimelineItem: DecorationTimelineItemProtocol, Equatable {
|
||||||
let id: TimelineItemIdentifier
|
let id: TimelineItemIdentifier
|
||||||
let text: String
|
let timestamp: Date
|
||||||
}
|
}
|
||||||
|
|||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:1a72db00d98af34232f15be756e5f2358920ea8b2c327949aece97755105779a
|
|
||||||
size 335736
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:da72b8fd77d8040d6d53cf4f8b02b36b1ebd4954b25c6487f7874df015f743eb
|
|
||||||
size 72305
|
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:472cdd02918531822503fbfb067e11ae24b8ad7ee510efa8b23d2dd17be920d1
|
||||||
|
size 84907
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:99cb5be30a32dd9b4d29be2c7c2788256b9a4afadd73b3a1ee9b1d8bb25a67d9
|
||||||
|
size 726687
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:de9881a48786a790f314fd25e1e5f76e2d91156c42d5d49337082c8023ac0de5
|
|
||||||
size 336616
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:cbdceca0d1cf597d6c1c31abb0146084fde5d183ab5acf8399badb6c1b6c7caf
|
|
||||||
size 73391
|
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:5882b637b51ce4e8d59dcec3779b2ebb37ba1f4db9f04d0a1cab50161b2baa71
|
||||||
|
size 92195
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:135f21259bdf8324ef4455b75f185988e22b2ed25d289aba19ee3f3e83ba7e88
|
||||||
|
size 733767
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:5e8c6353145a110c8ef00b32d6ce6edf76e093cc1aa3fdb1b62eef41af0b4e1d
|
|
||||||
size 795122
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:9e04f78541a56fcc68d4d96c4cb3c031c577fce926953710cedc515b3e0cd219
|
|
||||||
size 31710
|
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:71159e727f3bb47533674c0fcd05128e6ff561ba9d54ab30ba14bc40af2875a3
|
||||||
|
size 38785
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:fc96fc9a2ed0d4c6ddce7a4d84243f157f11f70d4d339bbd6fa59de874e26d0a
|
||||||
|
size 808526
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:696f15ca06f05895a003243b041107724143bbe0815147420d50ac475952c205
|
|
||||||
size 795870
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:9db638196c632227dc675d0d599d13e57d76ecf2cc7b310f94dab046316e7921
|
|
||||||
size 32636
|
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:28bb922273d38dea5b8444e16f2df180cdd73550f318112fa83919ef9b5db90c
|
||||||
|
size 42692
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:8581f27e004fae35ea17368cc70aa20b8add4cf2fe222275b5385152f9dd04e2
|
||||||
|
size 814086
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a8f68e3f488d0c469a5d368a2194eda034d3b8212be3a07049729dfb7eaf1e9e
|
oid sha256:5caa285f573ff5ed5a8c1c7e4df0015421b415fbb985865c0500c71814cce511
|
||||||
size 88436
|
size 94735
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a63c1e2da2e645d36385134db96726edfa1ececcbc10dfbcceead1597717a0a2
|
oid sha256:a834120a6cb86890e9a04130f722c45a184b232d8cecc982284b8b216848b228
|
||||||
size 90906
|
size 97871
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:832a4a1def3a55e42cbf7852948afe4a075350a9eaa53393634e81f9b9a62964
|
oid sha256:f5a8955e619a6eb923d9504da81039e28a8e4ab14d570b33154b997c9311804e
|
||||||
size 46672
|
size 51927
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:bd1d50456c64590a25778c41769444d428315d0ae36e0d030222a2701fda37e3
|
oid sha256:d682c9f79ae6ff7271c46f62f3fd453c30e65962efb5aa4bcdacf5566e99278c
|
||||||
size 49172
|
size 55130
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f891ab4ce086e3980af0f654017f6cf763a825e43a813ef1c9a443e2b8c58a47
|
oid sha256:d19fdc59c123919099ce464a0bb5478edb8df05e23609cd5dae1b430bcd469d1
|
||||||
size 293068
|
size 295900
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f1ae23a50e86f3da569b219703801262759688ebb5d37fe90502622778b9c9f9
|
oid sha256:015366f6fe40da0753b014c0be10d6f46478d36e76fef8ac0d03608c44c8665d
|
||||||
size 301242
|
size 304454
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d7a7290cf6e13af92c9b92257cd45148b7c96230ccc12042ed77f465f41902a8
|
oid sha256:e5fe32d96cc563369893c88d566260a465e5ae7f372b10f75f1cd6b9fa93877e
|
||||||
size 179656
|
size 181804
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:be0cce1962d880b7b07250a314ccf5e135439a0eb663e70df1d2557816a0477e
|
oid sha256:6332e9688b6bc74fd053be710d9fc160016ff08fe08d755420871e5a79ad6c24
|
||||||
size 177493
|
size 180191
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:6180f57ce6e161d79a0c52d1ab86518ae2227979fa806011f66c30869fbee81f
|
oid sha256:25fcc57afb1f80279f98ab65aa5e4ed741764e54407034aba329334c65e9878b
|
||||||
size 70278
|
size 71653
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:6180f57ce6e161d79a0c52d1ab86518ae2227979fa806011f66c30869fbee81f
|
oid sha256:4c244c3d942e34e4a54a07fef6d90f8e6b292ea4f8a79d88c14dd4a38ea0e8dd
|
||||||
size 70278
|
size 70977
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:b586f05cc0eabe44fbc3a1f9181ebf69b2593d6f9e046c115405d955dee5aff8
|
oid sha256:74d80c2fe62ebd088356b29f3fde14d32f81299a6a1b385ebdff4e115b600395
|
||||||
size 29598
|
size 30361
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:b586f05cc0eabe44fbc3a1f9181ebf69b2593d6f9e046c115405d955dee5aff8
|
oid sha256:7647c90ac369edd5bbfd9e0cbe5a871791f871d16c8db511f1d4b95a6bbb24a1
|
||||||
size 29598
|
size 31058
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:c626349c28842ccd498d55f01ac438258e7010503dc3dcfccfae461e47d84dab
|
oid sha256:562da5417d3d83843b934293c59fd15c729c947a11d85ee223f4e2884e575a80
|
||||||
size 280376
|
size 285688
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:6ebbdefa08186861eeeeed292843731a048a58072d78c1cb2ec83f48e963698d
|
oid sha256:9288fe8187bc7943a7523e0d161cbb74f4cd73a09c06e1e5cf3f19366a3e8084
|
||||||
size 278721
|
size 284078
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:de0ee487487365db8f8fe5e408ab77ed750da4124d728eb97ca7614e8792fe25
|
oid sha256:9c84f555adb18536c418b82b978cb7ef9175fbae00f18e40d7200f8d69a20533
|
||||||
size 287499
|
size 292926
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:e6b2cfaf19b5c650ea26d3d0e337a928d1d08459304936b6f6486b7de8d9d0a4
|
oid sha256:a75402f09fcec7dcb1132c47ca71e0a08e09e9898b2b10389aeed7bae75c4a46
|
||||||
size 286103
|
size 291518
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:763f8c4138ed5d0d535341a5b14f6c8fede9324eb088e68ab6874e95762a4d86
|
oid sha256:50f4a76320c1519daa984dd0b19fb965db8199dfd6786a8cf528228799585aeb
|
||||||
size 221889
|
size 226204
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:607c63a33ea20f0a24444f7dc808a57eb0a161c496a3187ce4b068534adacfca
|
oid sha256:95f55f16898bbcdfd599633456eb3436e4a79777fb9c6b93064a5b3e0bcd5dd4
|
||||||
size 219309
|
size 223638
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ac7908ffe89f4bbaed33ee75c62db9fa515dff746cf5b082a7523d21bfd0806e
|
oid sha256:d2b324da8fb2eb1d0e3a7ed3e9a4c2796622889d964645da75878e7ec2c53ef5
|
||||||
size 227388
|
size 232096
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:49fb9ce905498b08e70dd43077aab4913c8fdcdb3aead866f92caececcad3ada
|
oid sha256:a6b94fda33826a8bd56cc220f95496729df82fb17b2c5d34c76f07697010d670
|
||||||
size 224398
|
size 229096
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f891ab4ce086e3980af0f654017f6cf763a825e43a813ef1c9a443e2b8c58a47
|
oid sha256:d19fdc59c123919099ce464a0bb5478edb8df05e23609cd5dae1b430bcd469d1
|
||||||
size 293068
|
size 295900
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f1ae23a50e86f3da569b219703801262759688ebb5d37fe90502622778b9c9f9
|
oid sha256:015366f6fe40da0753b014c0be10d6f46478d36e76fef8ac0d03608c44c8665d
|
||||||
size 301242
|
size 304454
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d7a7290cf6e13af92c9b92257cd45148b7c96230ccc12042ed77f465f41902a8
|
oid sha256:e5fe32d96cc563369893c88d566260a465e5ae7f372b10f75f1cd6b9fa93877e
|
||||||
size 179656
|
size 181804
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:be0cce1962d880b7b07250a314ccf5e135439a0eb663e70df1d2557816a0477e
|
oid sha256:6332e9688b6bc74fd053be710d9fc160016ff08fe08d755420871e5a79ad6c24
|
||||||
size 177493
|
size 180191
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:daaa6d27b085fffcf1850d7aadcfef328196bb720b6ee1553fe716a03c07ec02
|
oid sha256:479b7eedb90b238436d89ff1c42ad786796692df62c02775c74475742fa3b386
|
||||||
size 292253
|
size 295544
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d609af9f7fa3a82716f496731d73e58cf92dd4e673d02c300a21f90d7f64e6f8
|
oid sha256:d02ff8334acfb26b5da2948d4b16a6418bdbd221f25d1366d4ef61038bf66d86
|
||||||
size 368793
|
size 372224
|
||||||
|
|||||||
@@ -441,7 +441,7 @@ private extension TextRoomTimelineItem {
|
|||||||
|
|
||||||
private extension SeparatorRoomTimelineItem {
|
private extension SeparatorRoomTimelineItem {
|
||||||
init(uniqueID: TimelineUniqueId) {
|
init(uniqueID: TimelineUniqueId) {
|
||||||
self.init(id: .virtual(uniqueID: uniqueID), text: "")
|
self.init(id: .virtual(uniqueID: uniqueID), timestamp: .mock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user