Room screen header (#86)
* #35 Create `ElementNavigationController` subclass * #35 Add encryption icons * #35 Add avatar and encryption badge image to the room screen view model * #35 Create `RoomHeaderView` class * #35 Replace room title with a RoomHeaderView instance in the toolbar * #35 Add changelog * #35 Introduce `UITestScreenIdentifier` and refactor ui tests * #35 Fix old tests * #35 add some tests for room screen * #35 Use svgs instead of pngs * #35 Fix PR remarks
This commit is contained in:
@@ -8,6 +8,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 */; };
|
||||
00AC53151BA23A90FAAE9FBF /* BugReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D607F47FDEF16CC63684BE0 /* BugReport.swift */; };
|
||||
00F3059B1E0CFCA019710C3E /* BugReportModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B516212D9FE785DDD5E490D1 /* BugReportModels.swift */; };
|
||||
01CB8ACFA5E143E89C168CA8 /* TimelineItemContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = B43AF03660F5FD4FFFA7F1CE /* TimelineItemContextMenu.swift */; };
|
||||
@@ -15,10 +16,12 @@
|
||||
02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 218AB05B4E3889731959C5F1 /* EventBasedTimelineItemProtocol.swift */; };
|
||||
03B8FEA668A5B76A93113BB1 /* MemberDetailProviderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C2ABC1A9B62BDB3D216E7FD /* MemberDetailProviderManager.swift */; };
|
||||
03CB204C52F18E24A5C3D219 /* UITestsAppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 967873B9E11828B67F64C89A /* UITestsAppCoordinator.swift */; };
|
||||
04A16B45228F7678A027C079 /* RoomHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 422724361B6555364C43281E /* RoomHeaderView.swift */; };
|
||||
05776B005C57E92582F0CF08 /* BuildSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F87116470221880017CF522 /* BuildSettings.swift */; };
|
||||
059173B3C77056C406906B6D /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = D4DA544B2520BFA65D6DB4BB /* target.yml */; };
|
||||
0602FA07557F580086782A9E /* UserIndicatorPresentationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FA072E995316CD18BC29313 /* UserIndicatorPresentationContext.swift */; };
|
||||
066A1E9B94723EE9F3038044 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47EBB5D698CE9A25BB553A2D /* Strings.swift */; };
|
||||
06E93B2E3B32740B40F47CC5 /* ElementNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF4B39D52CAE7D21D276ABEE /* ElementNavigationController.swift */; };
|
||||
072BA9DBA932374CCA300125 /* MessageComposerTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE6C10032A77AE7DC5AA4C50 /* MessageComposerTextField.swift */; };
|
||||
0B1F80C2BF7D223159FBA82C /* ImageAnonymizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6045E825AE900A92D61FEFF0 /* ImageAnonymizerTests.swift */; };
|
||||
0E8C480700870BB34A2A360F /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 78A5A8DE1E2B09C978C7F3B0 /* KeychainAccess */; };
|
||||
@@ -121,6 +124,7 @@
|
||||
7002C55A4C917F3715765127 /* MediaProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C888BCD78E2A55DCE364F160 /* MediaProviderProtocol.swift */; };
|
||||
7405B4824D45BA7C3D943E76 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D0CBC76C80E04345E11F2DB /* Application.swift */; };
|
||||
7434A7F02D587A920B376A9A /* LoginScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A43964330459965AF048A8C /* LoginScreenViewModelTests.swift */; };
|
||||
75D98001C5AC38B6A5CA897C /* UITestScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FD9D66B75292F2CC11AA4D2 /* UITestScreenIdentifier.swift */; };
|
||||
7756C4E90CABE6F14F7920A0 /* BugReportUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6FEA87EA3752203065ECE27 /* BugReportUITests.swift */; };
|
||||
77D7DAA41AAB36800C1F2E2D /* RoomTimelineProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095AED4CF56DFF3EB7BB84C8 /* RoomTimelineProviderProtocol.swift */; };
|
||||
77E192BA943B90F9F310CA23 /* WeakDictionaryKeyReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FFCC48E7F701B6C24484593 /* WeakDictionaryKeyReference.swift */; };
|
||||
@@ -336,6 +340,7 @@
|
||||
40B21E611DADDEF00307E7AC /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
|
||||
4110685D9CA159F3FD2D6BA1 /* TextRoomMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomMessage.swift; sourceTree = "<group>"; };
|
||||
4112D04077F6709C5CA0A13E /* FullscreenLoadingViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullscreenLoadingViewPresenter.swift; sourceTree = "<group>"; };
|
||||
422724361B6555364C43281E /* RoomHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomHeaderView.swift; sourceTree = "<group>"; };
|
||||
434522ED2BDED08759048077 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
4411C0DA0087A1CB143E96FA /* EventBrief.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBrief.swift; sourceTree = "<group>"; };
|
||||
4470B8CB654B097D807AA713 /* ToastViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastViewPresenter.swift; sourceTree = "<group>"; };
|
||||
@@ -384,6 +389,7 @@
|
||||
5F12E996BFBEB43815189ABF /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = uk.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
5F4134FEFE4EB55759017408 /* UserSessionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionProtocol.swift; sourceTree = "<group>"; };
|
||||
5F77E8010D41AA3F5F9A1FCA /* NavigationModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationModule.swift; sourceTree = "<group>"; };
|
||||
5FD9D66B75292F2CC11AA4D2 /* UITestScreenIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestScreenIdentifier.swift; sourceTree = "<group>"; };
|
||||
5FF214969B25BFCBF87B908B /* bn-BD */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "bn-BD"; path = "bn-BD.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||
6033779EB37259F27F938937 /* ClientProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientProxyProtocol.swift; sourceTree = "<group>"; };
|
||||
6045E825AE900A92D61FEFF0 /* ImageAnonymizerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageAnonymizerTests.swift; sourceTree = "<group>"; };
|
||||
@@ -539,6 +545,7 @@
|
||||
CED34C87277BA3CCC6B6EC7A /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
CF3EDF23226895776553F04A /* AppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = "<group>"; };
|
||||
CF47564C584F614B7287F3EB /* RootRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootRouter.swift; sourceTree = "<group>"; };
|
||||
CF4B39D52CAE7D21D276ABEE /* ElementNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementNavigationController.swift; sourceTree = "<group>"; };
|
||||
CF847B3C1873B8E81CEE7FAC /* SplashScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
D0A45283CF1DB96E583BECA6 /* ImageRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
D29EBCBFEC6FD0941749404D /* NavigationRouterStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRouterStore.swift; sourceTree = "<group>"; };
|
||||
@@ -1016,6 +1023,7 @@
|
||||
4E854E7CF531DAC5CBEBDC75 /* ListTableViewAdapter.swift */,
|
||||
E18CF12478983A5EB390FB26 /* MessageComposer.swift */,
|
||||
BE6C10032A77AE7DC5AA4C50 /* MessageComposerTextField.swift */,
|
||||
422724361B6555364C43281E /* RoomHeaderView.swift */,
|
||||
5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */,
|
||||
B43AF03660F5FD4FFFA7F1CE /* TimelineItemContextMenu.swift */,
|
||||
804F9B0FABE093C7284CD09B /* TimelineItemList.swift */,
|
||||
@@ -1248,6 +1256,7 @@
|
||||
49EAD710A2C16EFF7C3EA16F /* Benchmark.swift */,
|
||||
E5272BC4A60B6AD7553BACA1 /* BlurHashDecode.swift */,
|
||||
95CC95CD75B688E946438165 /* Coordinator.swift */,
|
||||
CF4B39D52CAE7D21D276ABEE /* ElementNavigationController.swift */,
|
||||
12A626D74BBE9F4A60763B45 /* ImageAnonymizer.swift */,
|
||||
F7B81C8227BBEA95CCE86037 /* MatrixEntitityRegex.swift */,
|
||||
44BBB96FAA2F0D53C507396B /* Extensions */,
|
||||
@@ -1301,6 +1310,7 @@
|
||||
EFFA5FD06AAAC4AF544B594E /* AppDelegate.swift */,
|
||||
3F87116470221880017CF522 /* BuildSettings.swift */,
|
||||
967873B9E11828B67F64C89A /* UITestsAppCoordinator.swift */,
|
||||
5FD9D66B75292F2CC11AA4D2 /* UITestScreenIdentifier.swift */,
|
||||
CCA431E6EDD71F7067B5F9E7 /* UITestsRootView.swift */,
|
||||
0787F81684E503024BD0C051 /* Services */,
|
||||
E59565F441830B19DBAE567C /* Screens */,
|
||||
@@ -1705,6 +1715,7 @@
|
||||
DCB781BD227CA958809AFADF /* Coordinator.swift in Sources */,
|
||||
C4F69156C31A447FEFF2A47C /* DTHTMLElement+AttributedStringBuilder.swift in Sources */,
|
||||
EE8491AD81F47DF3C192497B /* DecorationTimelineItemProtocol.swift in Sources */,
|
||||
06E93B2E3B32740B40F47CC5 /* ElementNavigationController.swift in Sources */,
|
||||
D8CFF02C2730EE5BC4F17ABF /* ElementToggleStyle.swift in Sources */,
|
||||
7C1A7B594B2F8143F0DD0005 /* ElementXAttributeScope.swift in Sources */,
|
||||
224A55EEAEECF5336B14A4A5 /* EmoteRoomMessage.swift in Sources */,
|
||||
@@ -1764,6 +1775,7 @@
|
||||
7D1DAAA364A9A29D554BD24E /* PlaceholderAvatarImage.swift in Sources */,
|
||||
BF35062D06888FA80BD139FF /* Presentable.swift in Sources */,
|
||||
53B9C2240C2F5533246EE230 /* RectangleToastView.swift in Sources */,
|
||||
04A16B45228F7678A027C079 /* RoomHeaderView.swift in Sources */,
|
||||
FE79E2BCCF69E8BF4D21E15A /* RoomMessageFactory.swift in Sources */,
|
||||
8D9F646387DF656EF91EE4CB /* RoomMessageFactoryProtocol.swift in Sources */,
|
||||
D0619D2E6B9C511190FBEB95 /* RoomMessageProtocol.swift in Sources */,
|
||||
@@ -1824,6 +1836,7 @@
|
||||
9CB5129C83F75921E5E28028 /* ToastViewState.swift in Sources */,
|
||||
36AC963F2F04069B7FF1AA0C /* UIConstants.swift in Sources */,
|
||||
0EE5EBA18BA1FE10254BB489 /* UIFont+AttributedStringBuilder.m in Sources */,
|
||||
004561D297DC8B9786AE136F /* UITestScreenIdentifier.swift in Sources */,
|
||||
03CB204C52F18E24A5C3D219 /* UITestsAppCoordinator.swift in Sources */,
|
||||
17CC4FB64F3A670F43ECBE5F /* UITestsRootView.swift in Sources */,
|
||||
8775F46AE3234A5A5688C19D /* UserIndicator.swift in Sources */,
|
||||
@@ -1859,6 +1872,7 @@
|
||||
490E606044B18985055FF690 /* SettingsUITests.swift in Sources */,
|
||||
A00DFC1DD3567B1EDC9F8D16 /* SplashScreenUITests.swift in Sources */,
|
||||
2E68C57E7D644E94778743D5 /* TemplateSimpleScreenUITests.swift in Sources */,
|
||||
75D98001C5AC38B6A5CA897C /* UITestScreenIdentifier.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
12
ElementX/Resources/Assets.xcassets/Images/Encryption/encryption_normal.imageset/Contents.json
vendored
Normal file
12
ElementX/Resources/Assets.xcassets/Images/Encryption/encryption_normal.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "encryption_normal.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.09949 11.4627C6.06108 11.4727 6.02775 11.481 6 11.4876C5.97225 11.481 5.93892 11.4727 5.90051 11.4627C5.76801 11.4282 5.57572 11.3731 5.34366 11.2926C4.87791 11.1309 4.2608 10.8694 3.64749 10.4708C2.42721 9.67759 1.25 8.3665 1.25 6.2025V1.91207L6 0.521L10.75 1.91207V6.2025C10.75 8.3665 9.57279 9.67759 8.35251 10.4708C7.7392 10.8694 7.12209 11.1309 6.65634 11.2926C6.42428 11.3731 6.23199 11.4282 6.09949 11.4627Z" fill="#17191C" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 563 B |
12
ElementX/Resources/Assets.xcassets/Images/Encryption/encryption_trusted.imageset/Contents.json
vendored
Normal file
12
ElementX/Resources/Assets.xcassets/Images/Encryption/encryption_trusted.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "encryption_trusted.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.09949 11.4627C6.06108 11.4727 6.02775 11.481 6 11.4876C5.97225 11.481 5.93892 11.4727 5.90051 11.4627C5.76801 11.4282 5.57572 11.3731 5.34366 11.2926C4.87791 11.1309 4.2608 10.8694 3.64749 10.4708C2.42721 9.67759 1.25 8.3665 1.25 6.2025V1.91207L6 0.521L10.75 1.91207V6.2025C10.75 8.3665 9.57279 9.67759 8.35251 10.4708C7.7392 10.8694 7.12209 11.1309 6.65634 11.2926C6.42428 11.3731 6.23199 11.4282 6.09949 11.4627Z" fill="#0DBD8B" stroke="white"/>
|
||||
<path d="M8.70785 3.4047C8.57285 3.2622 8.34785 3.2547 8.20535 3.3897L5.00285 6.3897L3.82535 5.5647C3.66785 5.4597 3.45035 5.4597 3.30035 5.5947C3.12035 5.7447 3.10535 6.0147 3.25535 6.1947L4.61285 7.7547C4.63535 7.7772 4.65785 7.8072 4.68785 7.8222C4.94285 8.0322 5.32535 7.9947 5.53535 7.7397L5.55785 7.7097L8.72285 3.8772C8.82785 3.7422 8.82785 3.5397 8.70785 3.4047Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 950 B |
12
ElementX/Resources/Assets.xcassets/Images/Encryption/encryption_warning.imageset/Contents.json
vendored
Normal file
12
ElementX/Resources/Assets.xcassets/Images/Encryption/encryption_warning.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "encryption_warning.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.09949 11.4627C6.06108 11.4727 6.02775 11.481 6 11.4876C5.97225 11.481 5.93892 11.4727 5.90051 11.4627C5.76801 11.4282 5.57572 11.3731 5.34366 11.2926C4.87791 11.1309 4.2608 10.8694 3.64749 10.4708C2.42721 9.67759 1.25 8.3665 1.25 6.2025V1.91207L6 0.521L10.75 1.91207V6.2025C10.75 8.3665 9.57279 9.67759 8.35251 10.4708C7.7392 10.8694 7.12209 11.1309 6.65634 11.2926C6.42428 11.3731 6.23199 11.4282 6.09949 11.4627Z" fill="#FF4B55" stroke="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.42634 4.03626C5.39822 3.71563 5.63447 3.43438 5.95509 3.41188C6.27009 3.38938 6.55134 3.62563 6.58509 3.94626V4.03626L6.40509 6.28626C6.38822 6.49438 6.21384 6.65188 6.00572 6.65188H5.97197C5.77509 6.63501 5.62322 6.48313 5.60634 6.28626L5.42634 4.03626ZM6.49439 7.75379C6.49439 8.02717 6.27278 8.24879 5.99939 8.24879C5.72601 8.24879 5.50439 8.02717 5.50439 7.75379C5.50439 7.48041 5.72601 7.25879 5.99939 7.25879C6.27278 7.25879 6.49439 7.48041 6.49439 7.75379Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -44,7 +44,7 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
|
||||
}
|
||||
|
||||
splashViewController = SplashViewController()
|
||||
mainNavigationController = UINavigationController(rootViewController: splashViewController)
|
||||
mainNavigationController = ElementNavigationController(rootViewController: splashViewController)
|
||||
window = UIWindow(frame: UIScreen.main.bounds)
|
||||
window.rootViewController = mainNavigationController
|
||||
window.tintColor = .element.accent
|
||||
@@ -251,7 +251,9 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
|
||||
memberDetailProvider: memberDetailProvider)
|
||||
|
||||
let parameters = RoomScreenCoordinatorParameters(timelineController: timelineController,
|
||||
roomName: roomProxy.displayName ?? roomProxy.name)
|
||||
roomName: roomProxy.displayName ?? roomProxy.name,
|
||||
roomAvatar: userSession.mediaProvider.imageFromURLString(roomProxy.avatarURL),
|
||||
roomEncryptionBadge: roomProxy.encryptionBadgeImage)
|
||||
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
||||
|
||||
add(childCoordinator: coordinator)
|
||||
@@ -333,7 +335,7 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
|
||||
|
||||
add(childCoordinator: coordinator)
|
||||
coordinator.start()
|
||||
let navController = UINavigationController(rootViewController: coordinator.toPresentable())
|
||||
let navController = ElementNavigationController(rootViewController: coordinator.toPresentable())
|
||||
navController.navigationBar.topItem?.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel,
|
||||
target: self,
|
||||
action: #selector(dismissBugReportScreen))
|
||||
|
||||
@@ -20,6 +20,9 @@ internal typealias AssetImageTypeAlias = ImageAsset.Image
|
||||
// swiftlint:disable identifier_name line_length nesting type_body_length type_name
|
||||
internal enum Asset {
|
||||
internal enum Images {
|
||||
internal static let encryptionNormal = ImageAsset(name: "Images/encryption_normal")
|
||||
internal static let encryptionTrusted = ImageAsset(name: "Images/encryption_trusted")
|
||||
internal static let encryptionWarning = ImageAsset(name: "Images/encryption_warning")
|
||||
internal static let splashScreenPage1 = ImageAsset(name: "Images/Splash Screen Page 1")
|
||||
internal static let splashScreenPage2 = ImageAsset(name: "Images/Splash Screen Page 2")
|
||||
internal static let splashScreenPage3 = ImageAsset(name: "Images/Splash Screen Page 3")
|
||||
|
||||
18
ElementX/Sources/Other/ElementNavigationController.swift
Normal file
18
ElementX/Sources/Other/ElementNavigationController.swift
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// ElementNavigationController.swift
|
||||
// ElementX
|
||||
//
|
||||
// Created by Ismail on 20.06.2022.
|
||||
// Copyright © 2022 Element. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class ElementNavigationController: UINavigationController {
|
||||
|
||||
override func viewWillLayoutSubviews() {
|
||||
super.viewWillLayoutSubviews()
|
||||
navigationBar.topItem?.backButtonDisplayMode = .minimal
|
||||
}
|
||||
|
||||
}
|
||||
@@ -44,7 +44,7 @@ class NavigationRouterStore: NavigationRouterStoreProtocol {
|
||||
return existingNavigationRouter
|
||||
}
|
||||
|
||||
let navigationRouter = NavigationRouter(navigationController: UINavigationController())
|
||||
let navigationRouter = NavigationRouter(navigationController: ElementNavigationController())
|
||||
return navigationRouter
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ import SwiftUI
|
||||
struct RoomScreenCoordinatorParameters {
|
||||
let timelineController: RoomTimelineControllerProtocol
|
||||
let roomName: String?
|
||||
let roomAvatar: UIImage?
|
||||
let roomEncryptionBadge: UIImage?
|
||||
}
|
||||
|
||||
final class RoomScreenCoordinator: Coordinator, Presentable {
|
||||
@@ -43,7 +45,9 @@ final class RoomScreenCoordinator: Coordinator, Presentable {
|
||||
|
||||
let viewModel = RoomScreenViewModel(timelineController: parameters.timelineController,
|
||||
timelineViewFactory: RoomTimelineViewFactory(),
|
||||
roomName: parameters.roomName)
|
||||
roomName: parameters.roomName,
|
||||
roomAvatar: parameters.roomAvatar,
|
||||
roomEncryptionBadge: parameters.roomEncryptionBadge)
|
||||
|
||||
let view = RoomScreen(context: viewModel.context)
|
||||
roomScreenViewModel = viewModel
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
enum RoomScreenViewModelAction {
|
||||
|
||||
@@ -35,6 +36,8 @@ enum RoomScreenViewAction {
|
||||
|
||||
struct RoomScreenViewState: BindableState {
|
||||
var roomTitle: String = ""
|
||||
var roomAvatar: UIImage?
|
||||
var roomEncryptionBadge: UIImage?
|
||||
var items: [RoomTimelineViewProvider] = []
|
||||
var isBackPaginating = false
|
||||
var bindings: RoomScreenViewStateBindings
|
||||
|
||||
@@ -31,11 +31,16 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
|
||||
init(timelineController: RoomTimelineControllerProtocol,
|
||||
timelineViewFactory: RoomTimelineViewFactoryProtocol,
|
||||
roomName: String?) {
|
||||
roomName: String?,
|
||||
roomAvatar: UIImage? = nil,
|
||||
roomEncryptionBadge: UIImage? = nil) {
|
||||
self.timelineController = timelineController
|
||||
self.timelineViewFactory = timelineViewFactory
|
||||
|
||||
super.init(initialViewState: RoomScreenViewState(roomTitle: roomName ?? "Unknown room 💥", bindings: RoomScreenViewStateBindings(composerText: "")))
|
||||
super.init(initialViewState: RoomScreenViewState(roomTitle: roomName ?? "Unknown room 💥",
|
||||
roomAvatar: roomAvatar,
|
||||
roomEncryptionBadge: roomEncryptionBadge,
|
||||
bindings: .init(composerText: "")))
|
||||
|
||||
timelineController.callbacks
|
||||
.receive(on: DispatchQueue.main)
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
//
|
||||
// RoomHeaderView.swift
|
||||
// ElementX
|
||||
//
|
||||
// Created by Ismail on 21.06.2022.
|
||||
// Copyright © 2022 Element. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
import Introspect
|
||||
|
||||
struct RoomHeaderView: View {
|
||||
|
||||
@ObservedObject var context: RoomScreenViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 8) {
|
||||
roomAvatar
|
||||
Text(context.viewState.roomTitle)
|
||||
.font(.element.headline)
|
||||
.accessibilityIdentifier("roomNameLabel")
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private var roomAvatar: some View {
|
||||
ZStack(alignment: .bottomTrailing) {
|
||||
roomAvatarImage
|
||||
.clipShape(Circle())
|
||||
|
||||
if let encryptionBadge = context.viewState.roomEncryptionBadge {
|
||||
Image(uiImage: encryptionBadge)
|
||||
.accessibilityIdentifier("encryptionBadgeIcon")
|
||||
}
|
||||
}
|
||||
.frame(width: 32.0, height: 32.0)
|
||||
}
|
||||
|
||||
@ViewBuilder private var roomAvatarImage: some View {
|
||||
if let avatar = context.viewState.roomAvatar {
|
||||
Image(uiImage: avatar)
|
||||
.resizable()
|
||||
.scaledToFill()
|
||||
.accessibilityIdentifier("roomAvatarImage")
|
||||
} else {
|
||||
PlaceholderAvatarImage(firstCharacter: String(context.viewState.roomTitle.first ?? Character("")))
|
||||
.accessibilityIdentifier("roomAvatarPlaceholderImage")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct RoomHeaderView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
bodyPlain.preferredColorScheme(.light)
|
||||
bodyPlain.preferredColorScheme(.dark)
|
||||
bodyEncrypted.preferredColorScheme(.light)
|
||||
bodyEncrypted.preferredColorScheme(.dark)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
static var bodyPlain: some View {
|
||||
let viewModel = RoomScreenViewModel(timelineController: MockRoomTimelineController(),
|
||||
timelineViewFactory: RoomTimelineViewFactory(),
|
||||
roomName: "Some Room name",
|
||||
roomAvatar: Asset.Images.appLogo.image
|
||||
)
|
||||
|
||||
RoomHeaderView(context: viewModel.context)
|
||||
.previewLayout(.sizeThatFits)
|
||||
.padding()
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
static var bodyEncrypted: some View {
|
||||
let viewModel = RoomScreenViewModel(timelineController: MockRoomTimelineController(),
|
||||
timelineViewFactory: RoomTimelineViewFactory(),
|
||||
roomName: "Some Room name",
|
||||
roomAvatar: Asset.Images.appLogo.image,
|
||||
roomEncryptionBadge: Asset.Images.encryptionTrusted.image
|
||||
)
|
||||
|
||||
RoomHeaderView(context: viewModel.context)
|
||||
.previewLayout(.sizeThatFits)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,12 @@ struct RoomScreen: View {
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.navigationTitle(context.viewState.roomTitle)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
RoomHeaderView(context: context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func sendMessage() {
|
||||
|
||||
@@ -83,6 +83,15 @@ class RoomProxy: RoomProxyProtocol {
|
||||
var avatarURL: String? {
|
||||
room.avatarUrl()
|
||||
}
|
||||
|
||||
var encryptionBadgeImage: UIImage? {
|
||||
guard isEncrypted else {
|
||||
return nil
|
||||
}
|
||||
|
||||
// return trusted image for now, should be updated after verification status known
|
||||
return Asset.Images.encryptionTrusted.image
|
||||
}
|
||||
|
||||
func loadAvatarURLForUserId(_ userId: String) async -> Result<String?, RoomProxyError> {
|
||||
await Task.detached { () -> Result<String?, RoomProxyError> in
|
||||
|
||||
40
ElementX/Sources/UITestScreenIdentifier.swift
Normal file
40
ElementX/Sources/UITestScreenIdentifier.swift
Normal file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// ScreenIdentifier.swift
|
||||
// ElementX
|
||||
//
|
||||
// Created by Ismail on 21.06.2022.
|
||||
// Copyright © 2022 Element. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum UITestScreenIdentifier: String {
|
||||
case login
|
||||
case simpleRegular
|
||||
case simpleUpgrade
|
||||
case settings
|
||||
case bugReport
|
||||
case bugReportWithScreenshot
|
||||
case splash
|
||||
case roomPlainNoAvatar
|
||||
case roomEncryptedWithAvatar
|
||||
}
|
||||
|
||||
extension UITestScreenIdentifier: CustomStringConvertible {
|
||||
var description: String {
|
||||
return rawValue.titlecased()
|
||||
}
|
||||
}
|
||||
|
||||
extension UITestScreenIdentifier: CaseIterable { }
|
||||
|
||||
private extension String {
|
||||
func titlecased() -> String {
|
||||
replacingOccurrences(of: "([A-Z])",
|
||||
with: " $1",
|
||||
options: .regularExpression,
|
||||
range: range(of: self))
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
.capitalized
|
||||
}
|
||||
}
|
||||
@@ -40,19 +40,45 @@ class UITestsAppCoordinator: Coordinator {
|
||||
}
|
||||
|
||||
private func mockScreens() -> [MockScreen] {
|
||||
[
|
||||
MockScreen(id: "Login screen", coordinator: LoginScreenCoordinator(parameters: .init())),
|
||||
MockScreen(id: "Simple Screen - Regular", coordinator: TemplateSimpleScreenCoordinator(parameters: .init(promptType: .regular))),
|
||||
MockScreen(id: "Simple Screen - Upgrade", coordinator: TemplateSimpleScreenCoordinator(parameters: .init(promptType: .upgrade))),
|
||||
MockScreen(id: "Settings screen", coordinator: SettingsCoordinator(parameters: .init(navigationRouter: NavigationRouter(navigationController: UINavigationController()), bugReportService: MockBugReportService()))),
|
||||
MockScreen(id: "Bug report screen", coordinator: BugReportCoordinator(parameters: .init(bugReportService: MockBugReportService(), screenshot: nil))),
|
||||
MockScreen(id: "Bug report screen with screenshot", coordinator: BugReportCoordinator(parameters: .init(bugReportService: MockBugReportService(), screenshot: Asset.Images.appLogo.image))),
|
||||
MockScreen(id: "Splash Screen", coordinator: SplashScreenCoordinator())
|
||||
]
|
||||
UITestScreenIdentifier.allCases.map { MockScreen(id: $0) }
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
struct MockScreen: Identifiable {
|
||||
let id: String
|
||||
let coordinator: Coordinator & Presentable
|
||||
let id: UITestScreenIdentifier
|
||||
var coordinator: Coordinator & Presentable {
|
||||
switch id {
|
||||
case .login:
|
||||
return LoginScreenCoordinator(parameters: .init())
|
||||
case .simpleRegular:
|
||||
return TemplateSimpleScreenCoordinator(parameters: .init(promptType: .regular))
|
||||
case .simpleUpgrade:
|
||||
return TemplateSimpleScreenCoordinator(parameters: .init(promptType: .upgrade))
|
||||
case .settings:
|
||||
let router = NavigationRouter(navigationController: ElementNavigationController())
|
||||
return SettingsCoordinator(parameters: .init(navigationRouter: router,
|
||||
bugReportService: MockBugReportService()))
|
||||
case .bugReport:
|
||||
return BugReportCoordinator(parameters: .init(bugReportService: MockBugReportService(),
|
||||
screenshot: nil))
|
||||
case .bugReportWithScreenshot:
|
||||
return BugReportCoordinator(parameters: .init(bugReportService: MockBugReportService(),
|
||||
screenshot: Asset.Images.appLogo.image))
|
||||
case .splash:
|
||||
return SplashScreenCoordinator()
|
||||
case .roomPlainNoAvatar:
|
||||
let params = RoomScreenCoordinatorParameters(timelineController: MockRoomTimelineController(),
|
||||
roomName: "Some room name",
|
||||
roomAvatar: nil,
|
||||
roomEncryptionBadge: nil)
|
||||
return RoomScreenCoordinator(parameters: params)
|
||||
case .roomEncryptedWithAvatar:
|
||||
let params = RoomScreenCoordinatorParameters(timelineController: MockRoomTimelineController(),
|
||||
roomName: "Some room name",
|
||||
roomAvatar: Asset.Images.appLogo.image,
|
||||
roomEncryptionBadge: Asset.Images.encryptionTrusted.image)
|
||||
return RoomScreenCoordinator(parameters: params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,14 +11,15 @@ import SwiftUI
|
||||
struct UITestsRootView: View {
|
||||
|
||||
let mockScreens: [MockScreen]
|
||||
var selectionCallback: ((String) -> Void)?
|
||||
var selectionCallback: ((UITestScreenIdentifier) -> Void)?
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
List(mockScreens) { coordinator in
|
||||
Button(coordinator.id) {
|
||||
Button(coordinator.id.description) {
|
||||
selectionCallback?(coordinator.id)
|
||||
}
|
||||
.accessibilityIdentifier(coordinator.id.rawValue)
|
||||
}
|
||||
.listStyle(.plain)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import ElementX
|
||||
class TemplateSimpleScreenUITests: XCTestCase {
|
||||
func testRegularScreen() {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier("Simple Screen - Regular")
|
||||
app.goToScreenWithIdentifier(.simpleRegular)
|
||||
|
||||
let title = app.staticTexts["title"]
|
||||
XCTAssert(title.exists)
|
||||
@@ -30,7 +30,7 @@ class TemplateSimpleScreenUITests: XCTestCase {
|
||||
|
||||
func testUpgradeScreen() {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier("Simple Screen - Upgrade")
|
||||
app.goToScreenWithIdentifier(.simpleUpgrade)
|
||||
|
||||
let title = app.staticTexts["title"]
|
||||
XCTAssert(title.exists)
|
||||
|
||||
@@ -18,8 +18,8 @@ struct Application {
|
||||
}
|
||||
|
||||
extension XCUIApplication {
|
||||
func goToScreenWithIdentifier(_ identifier: String) {
|
||||
let button = self.buttons[identifier]
|
||||
func goToScreenWithIdentifier(_ identifier: UITestScreenIdentifier) {
|
||||
let button = self.buttons[identifier.rawValue]
|
||||
let lastLabel = staticTexts["lastItem"]
|
||||
|
||||
while !button.isHittable && !lastLabel.isHittable {
|
||||
|
||||
@@ -21,7 +21,7 @@ class BugReportUITests: XCTestCase {
|
||||
|
||||
func testInitialStateComponents() {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier("Bug report screen")
|
||||
app.goToScreenWithIdentifier(.bugReport)
|
||||
|
||||
XCTAssert(app.navigationBars["Bug report"].exists)
|
||||
XCTAssert(app.staticTexts["reportBugDescription"].exists)
|
||||
@@ -40,7 +40,7 @@ class BugReportUITests: XCTestCase {
|
||||
|
||||
func testToggleSendingLogs() {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier("Bug report screen")
|
||||
app.goToScreenWithIdentifier(.bugReport)
|
||||
|
||||
app.switches["sendLogsToggle"].tap()
|
||||
|
||||
@@ -51,7 +51,7 @@ class BugReportUITests: XCTestCase {
|
||||
|
||||
func testReportText() {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier("Bug report screen")
|
||||
app.goToScreenWithIdentifier(.bugReport)
|
||||
|
||||
// type 4 chars
|
||||
app.textViews["reportTextView"].tap()
|
||||
@@ -66,7 +66,7 @@ class BugReportUITests: XCTestCase {
|
||||
|
||||
func testInitialStateComponentsWithScreenshot() {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier("Bug report screen with screenshot")
|
||||
app.goToScreenWithIdentifier(.bugReportWithScreenshot)
|
||||
|
||||
XCTAssert(app.navigationBars["Bug report"].exists)
|
||||
XCTAssert(app.staticTexts["reportBugDescription"].exists)
|
||||
|
||||
@@ -19,7 +19,7 @@ import XCTest
|
||||
class LoginScreenUITests: XCTestCase {
|
||||
func testInitialStateComponents() {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier("Login screen")
|
||||
app.goToScreenWithIdentifier(.login)
|
||||
|
||||
XCTAssert(app.buttons["Login"].exists)
|
||||
XCTAssert(app.textFields["Username"].exists)
|
||||
|
||||
@@ -15,3 +15,30 @@
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import ElementX
|
||||
|
||||
@MainActor
|
||||
class RoomScreenUITests: XCTestCase {
|
||||
|
||||
func testPlainNoAvatar() async throws {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.roomPlainNoAvatar)
|
||||
|
||||
try await Task.sleep(nanoseconds: 400_000_000)
|
||||
|
||||
XCTAssert(app.staticTexts["roomNameLabel"].exists)
|
||||
XCTAssert(app.staticTexts["roomAvatarPlaceholderImage"].exists)
|
||||
XCTAssertFalse(app.images["encryptionBadgeIcon"].exists)
|
||||
}
|
||||
|
||||
func testEncryptedWithAvatar() async throws {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.roomEncryptedWithAvatar)
|
||||
|
||||
try await Task.sleep(nanoseconds: 400_000_000)
|
||||
|
||||
XCTAssert(app.staticTexts["roomNameLabel"].exists)
|
||||
XCTAssert(app.images["roomAvatarImage"].exists)
|
||||
XCTAssert(app.images["encryptionBadgeIcon"].exists)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ class SettingsUITests: XCTestCase {
|
||||
|
||||
func testInitialStateComponents() {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier("Settings screen")
|
||||
app.goToScreenWithIdentifier(.settings)
|
||||
|
||||
XCTAssert(app.navigationBars["Settings"].exists)
|
||||
XCTAssert(app.buttons["reportBugButton"].exists)
|
||||
|
||||
@@ -20,7 +20,7 @@ import XCTest
|
||||
class SplashScreenUITests: XCTestCase {
|
||||
func testInitialStateComponents() {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier("Splash Screen")
|
||||
app.goToScreenWithIdentifier(.splash)
|
||||
|
||||
let getStartedButton = app.buttons["Get started"]
|
||||
XCTAssertTrue(getStartedButton.exists, "The primary action button should be shown.")
|
||||
@@ -28,7 +28,7 @@ class SplashScreenUITests: XCTestCase {
|
||||
|
||||
func testSwipingBetweenPages() async throws {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier("Splash Screen")
|
||||
app.goToScreenWithIdentifier(.splash)
|
||||
|
||||
// Given the splash screen in its initial state.
|
||||
let page1TitleText = app.staticTexts["Own your conversations."]
|
||||
|
||||
@@ -41,3 +41,4 @@ targets:
|
||||
- path: ../SupportingFiles
|
||||
- path: ../../Tools/Scripts/Templates/SimpleScreenExample/Tests/UI
|
||||
- path: ../../ElementX/Sources/BuildSettings.swift
|
||||
- path: ../../ElementX/Sources/UITestScreenIdentifier.swift
|
||||
|
||||
@@ -28,22 +28,6 @@ class HomeScreenViewModelTests: XCTestCase {
|
||||
context = viewModel.context
|
||||
}
|
||||
|
||||
@MainActor func testLogout() async throws {
|
||||
var correctResult = false
|
||||
viewModel.callback = { result in
|
||||
switch result {
|
||||
case .logout:
|
||||
correctResult = true
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
context.send(viewAction: .logout)
|
||||
await Task.yield()
|
||||
XCTAssert(correctResult)
|
||||
}
|
||||
|
||||
@MainActor func testSelectRoom() async throws {
|
||||
let mockRoomId = "mock_room_id"
|
||||
var correctResult = false
|
||||
|
||||
@@ -29,8 +29,20 @@ class SettingsViewModelTests: XCTestCase {
|
||||
context = viewModel.context
|
||||
}
|
||||
|
||||
func testInitialState() {
|
||||
XCTAssert(context.viewState.crashButtonVisible)
|
||||
@MainActor func testLogout() async throws {
|
||||
var correctResult = false
|
||||
viewModel.callback = { result in
|
||||
switch result {
|
||||
case .logout:
|
||||
correctResult = true
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
context.send(viewAction: .logout)
|
||||
await Task.yield()
|
||||
XCTAssert(correctResult)
|
||||
}
|
||||
|
||||
func testReportBug() async throws {
|
||||
|
||||
1
changelog.d/35.change
Normal file
1
changelog.d/35.change
Normal file
@@ -0,0 +1 @@
|
||||
Room: Add header view containing room avatar and encryption badge.
|
||||
Reference in New Issue
Block a user