Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redacted events in timeline #199

Merged
merged 6 commits into from
Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions ElementX.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
0033481EE363E4914295F188 /* LocalizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C070FD43DC6BF4E50217965A /* LocalizationTests.swift */; };
004561D297DC8B9786AE136F /* UITestScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FD9D66B75292F2CC11AA4D2 /* UITestScreenIdentifier.swift */; };
00EA14F62DCEF62CDE4808D6 /* RedactedRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B577F829C693B8DFB7014FD /* RedactedRoomTimelineItem.swift */; };
00F3059B1E0CFCA019710C3E /* BugReportModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B516212D9FE785DDD5E490D1 /* BugReportModels.swift */; };
01CB8ACFA5E143E89C168CA8 /* TimelineItemContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = B43AF03660F5FD4FFFA7F1CE /* TimelineItemContextMenu.swift */; };
01F4A40C1EDCEC8DC4EC9CFA /* WeakDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 109C0201D8CB3F947340DC80 /* WeakDictionary.swift */; };
Expand Down Expand Up @@ -39,6 +40,7 @@
1281625B25371BE53D36CB3A /* SeparatorRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1ED7E89865201EE7D53E6DA /* SeparatorRoomTimelineItem.swift */; };
12F70C493FB69F4D7E9A37EA /* NavigationRouterStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29EBCBFEC6FD0941749404D /* NavigationRouterStore.swift */; };
132D241B09F9044711FD70A5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 91DE43B8815918E590912DDA /* InfoPlist.strings */; };
13853973A5E24374FCEDE8A3 /* RedactedRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F2A7A4E3F5060F52ACFFB0 /* RedactedRoomTimelineView.swift */; };
13C77FDF17C4C6627CFFC205 /* RoomTimelineItemFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D25A35764C7B3DB78954AB5 /* RoomTimelineItemFactoryProtocol.swift */; };
149D1942DC005D0485FB8D93 /* LoggingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DC1943ADE6A62ED5129D7C8 /* LoggingTests.swift */; };
152AE2B8650FB23AFD2E28B9 /* MockAuthenticationServiceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65C2B80DD0BF6F10BB5FA922 /* MockAuthenticationServiceProxy.swift */; };
Expand Down Expand Up @@ -650,6 +652,7 @@
99DE232F24EAD72A3DF7EF1A /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = kab; path = kab.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
9A68BCE6438873D2661D93D0 /* BugReportServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportServiceProtocol.swift; sourceTree = "<group>"; };
9ABAECB0CA5FF8F8E6F10DD7 /* RoomTimelineProviderItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProviderItem.swift; sourceTree = "<group>"; };
9B577F829C693B8DFB7014FD /* RedactedRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedactedRoomTimelineItem.swift; sourceTree = "<group>"; };
9C4048041C1A6B20CB97FD18 /* TestMeasurementParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestMeasurementParser.swift; sourceTree = "<group>"; };
9C5E81214D27A6B898FC397D /* ElementX.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ElementX.entitlements; sourceTree = "<group>"; };
9C7F7DE62D33C6A26CBFCD72 /* IntegrationTests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = IntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -731,6 +734,7 @@
C6FEA87EA3752203065ECE27 /* BugReportUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportUITests.swift; sourceTree = "<group>"; };
C88508B6F7974CFABEC4B261 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
C888BCD78E2A55DCE364F160 /* MediaProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProviderProtocol.swift; sourceTree = "<group>"; };
C8F2A7A4E3F5060F52ACFFB0 /* RedactedRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedactedRoomTimelineView.swift; sourceTree = "<group>"; };
C91A6BC1A54CDB598EE2A81B /* UserIndicatorQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorQueue.swift; sourceTree = "<group>"; };
C95ADE8D9527523572532219 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = hu; path = hu.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
C9A86C95340248A8B7BA9A43 /* AnalyticsPromptViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptViewModelProtocol.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1325,6 +1329,7 @@
F77C060C2ACC4CB7336A29E7 /* EmoteRoomTimelineItem.swift */,
1A63815AD6A5C306453342F2 /* ImageRoomTimelineItem.swift */,
4F49CDE349C490D617332770 /* NoticeRoomTimelineItem.swift */,
9B577F829C693B8DFB7014FD /* RedactedRoomTimelineItem.swift */,
289FA233E896FBC5956C67E0 /* RoomTimelineItemProperties.swift */,
A1ED7E89865201EE7D53E6DA /* SeparatorRoomTimelineItem.swift */,
F6A8C632CEF4600107792899 /* TextRoomTimelineItem.swift */,
Expand Down Expand Up @@ -1645,6 +1650,7 @@
D0A45283CF1DB96E583BECA6 /* ImageRoomTimelineView.swift */,
B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */,
0950733DD4BA83EEE752E259 /* PlaceholderAvatarImage.swift */,
C8F2A7A4E3F5060F52ACFFB0 /* RedactedRoomTimelineView.swift */,
6390A6DC140CA3D6865A66FF /* SeparatorRoomTimelineView.swift */,
F9E785D5137510481733A3E8 /* TextRoomTimelineView.swift */,
);
Expand Down Expand Up @@ -2414,6 +2420,8 @@
BF35062D06888FA80BD139FF /* Presentable.swift in Sources */,
C76892321558E75101E68ED6 /* ReadableFrameModifier.swift in Sources */,
53B9C2240C2F5533246EE230 /* RectangleToastView.swift in Sources */,
00EA14F62DCEF62CDE4808D6 /* RedactedRoomTimelineItem.swift in Sources */,
13853973A5E24374FCEDE8A3 /* RedactedRoomTimelineView.swift in Sources */,
04A16B45228F7678A027C079 /* RoomHeaderView.swift in Sources */,
FE79E2BCCF69E8BF4D21E15A /* RoomMessageFactory.swift in Sources */,
8D9F646387DF656EF91EE4CB /* RoomMessageFactoryProtocol.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation
import SwiftUI

struct RedactedRoomTimelineView: View {
let timelineItem: RedactedRoomTimelineItem

var body: some View {
TimelineStyler(timelineItem: timelineItem) {
HStack {
Image(systemName: "trash")
FormattedBodyText(isOutgoing: timelineItem.isOutgoing, text: timelineItem.text)
}
}
.id(timelineItem.id)
}
}

struct RedactedRoomTimelineView_Previews: PreviewProvider {
static var previews: some View {
body.preferredColorScheme(.light)
body.preferredColorScheme(.dark)
}

@ViewBuilder
static var body: some View {
VStack(alignment: .leading, spacing: 20.0) {
RedactedRoomTimelineView(timelineItem: itemWith(text: ElementL10n.eventRedacted,
timestamp: "Later",
senderId: "Anne"))
}
.padding()
}

private static func itemWith(text: String, timestamp: String, senderId: String) -> RedactedRoomTimelineItem {
RedactedRoomTimelineItem(id: UUID().uuidString,
text: text,
timestamp: timestamp,
shouldShowSenderDetails: true,
inGroupState: .single,
isOutgoing: false,
senderId: senderId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {

switch item {
case .event(let eventItem):
guard eventItem.isMessage else { break } // To be handled in the future
guard eventItem.isMessage || eventItem.isRedacted else { break } // To be handled in the future

newTimelineItems.append(await timelineItemFactory.buildTimelineItemFor(eventItem: eventItem,
showSenderDetails: inGroupState.shouldShowSenderDetails,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ struct EventTimelineItem {
var content: TimelineItemContent {
item.content()
}

var isRedacted: Bool {
content.isRedactedMessage()
}

var isOwn: Bool {
item.isOwn()
}

var sender: String {
item.sender()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation
import UIKit

struct RedactedRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Equatable {
let id: String
let text: String
let timestamp: String
let shouldShowSenderDetails: Bool
let inGroupState: TimelineItemInGroupState
let isOutgoing: Bool

let senderId: String
var senderDisplayName: String?
var senderAvatar: UIImage?

var properties = RoomTimelineItemProperties()
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,16 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
func buildTimelineItemFor(eventItem: EventTimelineItem,
showSenderDetails: Bool,
inGroupState: TimelineItemInGroupState) async -> RoomTimelineItemProtocol {
guard let messageContent = eventItem.content.asMessage() else { fatalError("Must be a message for now.") }
let displayName = roomProxy.displayNameForUserId(eventItem.sender)
let avatarURL = roomProxy.avatarURLStringForUserId(eventItem.sender)
let avatarImage = mediaProvider.imageFromURLString(avatarURL, size: MediaProviderDefaultAvatarSize)
let isOutgoing = eventItem.sender == userID
let isOutgoing = eventItem.isOwn

if eventItem.isRedacted {
return buildRedactedTimelineItemFromEvent(eventItem, isOutgoing, showSenderDetails, inGroupState, displayName, avatarImage)
}

guard let messageContent = eventItem.content.asMessage() else { fatalError("Must be a message for now.") }

switch messageContent.msgtype() {
case .text(content: let content):
Expand All @@ -66,6 +71,24 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {

// swiftformat:disable function_parameter_count
// swiftlint:disable function_parameter_count
private func buildRedactedTimelineItemFromEvent(_ event: EventTimelineItem,
_ isOutgoing: Bool,
_ showSenderDetails: Bool,
_ inGroupState: TimelineItemInGroupState,
_ displayName: String?,
_ avatarImage: UIImage?) -> RoomTimelineItemProtocol {
RedactedRoomTimelineItem(id: event.id,
text: ElementL10n.eventRedacted,
timestamp: event.originServerTs.formatted(date: .omitted, time: .shortened),
shouldShowSenderDetails: showSenderDetails,
inGroupState: inGroupState,
isOutgoing: isOutgoing,
senderId: event.sender,
senderDisplayName: displayName,
senderAvatar: avatarImage,
properties: RoomTimelineItemProperties())
}

private func buildFallbackTimelineItem(_ item: EventTimelineItem,
_ isOutgoing: Bool,
_ showSenderDetails: Bool,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ struct RoomTimelineViewFactory: RoomTimelineViewFactoryProtocol {
return .notice(item)
case let item as EmoteRoomTimelineItem:
return .emote(item)
case let item as RedactedRoomTimelineItem:
return .redacted(item)
default:
fatalError("Unknown timeline item")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ enum RoomTimelineViewProvider: Identifiable, Equatable {
case image(ImageRoomTimelineItem)
case emote(EmoteRoomTimelineItem)
case notice(NoticeRoomTimelineItem)
case redacted(RedactedRoomTimelineItem)

var id: String {
switch self {
Expand All @@ -36,6 +37,8 @@ enum RoomTimelineViewProvider: Identifiable, Equatable {
return item.id
case .notice(let item):
return item.id
case .redacted(let item):
return item.id
}
}
}
Expand All @@ -53,6 +56,8 @@ extension RoomTimelineViewProvider: View {
EmoteRoomTimelineView(timelineItem: item)
case .notice(let item):
NoticeRoomTimelineView(timelineItem: item)
case .redacted(let item):
RedactedRoomTimelineView(timelineItem: item)
}
}
}
4 changes: 3 additions & 1 deletion UnitTests/Sources/SettingsViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ class SettingsViewModelTests: XCTestCase {
var context: SettingsViewModelType.Context!

@MainActor override func setUpWithError() throws {
viewModel = SettingsViewModel()
let userSession = MockUserSession(clientProxy: MockClientProxy(userIdentifier: ""),
mediaProvider: MockMediaProvider())
viewModel = SettingsViewModel(withUserSession: userSession)
context = viewModel.context
}

Expand Down
1 change: 1 addition & 0 deletions changelog.d/pr-199.change
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Include redacted events in the timeline.