diff --git a/AUTHORS.rst b/AUTHORS.rst index 5a02bcaf8..e3dfda228 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -26,4 +26,7 @@ Denis Morozov Aram Sargsyan * PR #1341: Read receipts details + +Vivian Lim + * PR #1513 Return key on hardware keyboards now sends messages \ No newline at end of file diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 4d13550ad..4c6145810 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -75,13 +75,16 @@ 32AE61F21F0D2183007255F4 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 32AE61EC1F0D2183007255F4 /* InfoPlist.strings */; }; 32AE61F31F0D2183007255F4 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 32AE61EE1F0D2183007255F4 /* Localizable.strings */; }; 32AE61F41F0D2183007255F4 /* Vector.strings in Resources */ = {isa = PBXBuildFile; fileRef = 32AE61F01F0D2183007255F4 /* Vector.strings */; }; + 32C2356F1F7B871800E38FC5 /* WidgetPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C2356E1F7B871800E38FC5 /* WidgetPickerViewController.m */; }; 32D392181EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D392161EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.m */; }; 32D392191EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32D392171EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.xib */; }; 32E84FA11F6BD32700CA0B89 /* apps-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 32E84F9E1F6BD32700CA0B89 /* apps-icon.png */; }; 32E84FA21F6BD32700CA0B89 /* apps-icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 32E84F9F1F6BD32700CA0B89 /* apps-icon@2x.png */; }; 32E84FA31F6BD32700CA0B89 /* apps-icon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 32E84FA01F6BD32700CA0B89 /* apps-icon@3x.png */; }; + 32F3AE1A1F6FF4E600F0F004 /* WidgetViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F3AE191F6FF4E600F0F004 /* WidgetViewController.m */; }; 32FD0A3D1EB0CD9B0072B066 /* BugReportViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FD0A3B1EB0CD9B0072B066 /* BugReportViewController.m */; }; 32FD0A3E1EB0CD9B0072B066 /* BugReportViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32FD0A3C1EB0CD9B0072B066 /* BugReportViewController.xib */; }; + 83711A7C1F6F8E7D008F0D4D /* KeyboardGrowingTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 83711A7B1F6F8E7D008F0D4D /* KeyboardGrowingTextView.m */; }; 92324BE31F4F66D3009DE194 /* IncomingCallView.m in Sources */ = {isa = PBXBuildFile; fileRef = 92324BE21F4F66D3009DE194 /* IncomingCallView.m */; }; 92324BE61F4F6A60009DE194 /* CircleButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 92324BE51F4F6A60009DE194 /* CircleButton.m */; }; A27ECCE3FC4971745D2CB78D /* libPods-RiotShareExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7246451C668D6782166E22EC /* libPods-RiotShareExtension.a */; }; @@ -497,6 +500,7 @@ F0B4CBAC1F41F090008E99C5 /* EncryptionInfoView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F0B4CBAB1F41F090008E99C5 /* EncryptionInfoView.xib */; }; F0B4CBB11F4215E3008E99C5 /* EventDetailsView.m in Sources */ = {isa = PBXBuildFile; fileRef = F0B4CBAF1F4215E3008E99C5 /* EventDetailsView.m */; }; F0B4CBB21F4215E3008E99C5 /* EventDetailsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F0B4CBB01F4215E3008E99C5 /* EventDetailsView.xib */; }; + F0B5430A1F70FF8F00D29293 /* back_icon@4x.png in Resources */ = {isa = PBXBuildFile; fileRef = F0B543091F70FF8F00D29293 /* back_icon@4x.png */; }; F0B7A8B11F475783006E27D2 /* RoomsListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F0B7A8AF1F4756A5006E27D2 /* RoomsListViewController.xib */; }; F0D2ADA11F6AA5FD00A7097D /* MXRoomSummary+Riot.m in Sources */ = {isa = PBXBuildFile; fileRef = F0D2ADA01F6AA5FD00A7097D /* MXRoomSummary+Riot.m */; }; F0D869EB1EC455A100BB0A2B /* create_direct_chat.png in Resources */ = {isa = PBXBuildFile; fileRef = F0D869E81EC455A100BB0A2B /* create_direct_chat.png */; }; @@ -652,17 +656,22 @@ 32AE61ED1F0D2183007255F4 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = InfoPlist.strings; sourceTree = ""; }; 32AE61EF1F0D2183007255F4 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = Localizable.strings; sourceTree = ""; }; 32AE61F11F0D2183007255F4 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = Vector.strings; sourceTree = ""; }; + 32C2356D1F7B871800E38FC5 /* WidgetPickerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WidgetPickerViewController.h; sourceTree = ""; }; + 32C2356E1F7B871800E38FC5 /* WidgetPickerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WidgetPickerViewController.m; sourceTree = ""; }; 32D392151EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectoryServerDetailTableViewCell.h; sourceTree = ""; }; 32D392161EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DirectoryServerDetailTableViewCell.m; sourceTree = ""; }; 32D392171EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DirectoryServerDetailTableViewCell.xib; sourceTree = ""; }; 32E84F9E1F6BD32700CA0B89 /* apps-icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "apps-icon.png"; sourceTree = ""; }; 32E84F9F1F6BD32700CA0B89 /* apps-icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "apps-icon@2x.png"; sourceTree = ""; }; 32E84FA01F6BD32700CA0B89 /* apps-icon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "apps-icon@3x.png"; sourceTree = ""; }; + 32F3AE181F6FF4E600F0F004 /* WidgetViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WidgetViewController.h; sourceTree = ""; }; + 32F3AE191F6FF4E600F0F004 /* WidgetViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WidgetViewController.m; sourceTree = ""; }; 32FD0A3A1EB0CD9B0072B066 /* BugReportViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BugReportViewController.h; sourceTree = ""; }; 32FD0A3B1EB0CD9B0072B066 /* BugReportViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugReportViewController.m; sourceTree = ""; }; 32FD0A3C1EB0CD9B0072B066 /* BugReportViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BugReportViewController.xib; sourceTree = ""; }; 7246451C668D6782166E22EC /* libPods-RiotShareExtension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RiotShareExtension.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 765F5104DB3EC39713DEB3A4 /* Pods-RiotShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotShareExtension.release.xcconfig"; path = "Pods/Target Support Files/Pods-RiotShareExtension/Pods-RiotShareExtension.release.xcconfig"; sourceTree = ""; }; + 83711A7B1F6F8E7D008F0D4D /* KeyboardGrowingTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KeyboardGrowingTextView.m; sourceTree = ""; }; 839BB91240D350D5607D55BA /* Pods-Riot.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Riot.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Riot/Pods-Riot.debug.xcconfig"; sourceTree = ""; }; 92324BE11F4F66D3009DE194 /* IncomingCallView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IncomingCallView.h; sourceTree = ""; }; 92324BE21F4F66D3009DE194 /* IncomingCallView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IncomingCallView.m; sourceTree = ""; }; @@ -1205,6 +1214,7 @@ F0B4CBAE1F4215E3008E99C5 /* EventDetailsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventDetailsView.h; sourceTree = ""; }; F0B4CBAF1F4215E3008E99C5 /* EventDetailsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EventDetailsView.m; sourceTree = ""; }; F0B4CBB01F4215E3008E99C5 /* EventDetailsView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EventDetailsView.xib; sourceTree = ""; }; + F0B543091F70FF8F00D29293 /* back_icon@4x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "back_icon@4x.png"; sourceTree = ""; }; F0B7A8AF1F4756A5006E27D2 /* RoomsListViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RoomsListViewController.xib; sourceTree = ""; }; F0D2AD9F1F6AA5FD00A7097D /* MXRoomSummary+Riot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MXRoomSummary+Riot.h"; sourceTree = ""; }; F0D2ADA01F6AA5FD00A7097D /* MXRoomSummary+Riot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MXRoomSummary+Riot.m"; sourceTree = ""; }; @@ -1367,11 +1377,15 @@ 3233F72D1F31F47E006ACA81 /* Widgets */ = { isa = PBXGroup; children = ( + 32F3AE181F6FF4E600F0F004 /* WidgetViewController.h */, + 32F3AE191F6FF4E600F0F004 /* WidgetViewController.m */, 32935CAF1F6056FD006888C8 /* IntegrationManagerViewController.h */, 32935CB01F6056FD006888C8 /* IntegrationManagerViewController.m */, 3233F72E1F31F4BF006ACA81 /* JitsiViewController.h */, 3233F72F1F31F4BF006ACA81 /* JitsiViewController.m */, 3233F7301F31F4BF006ACA81 /* JitsiViewController.xib */, + 32C2356D1F7B871800E38FC5 /* WidgetPickerViewController.h */, + 32C2356E1F7B871800E38FC5 /* WidgetPickerViewController.m */, ); path = Widgets; sourceTree = ""; @@ -1602,6 +1616,7 @@ F083BB211E7009EC00A9B29C /* back_icon.png */, F083BB221E7009EC00A9B29C /* back_icon@2x.png */, F083BB231E7009EC00A9B29C /* back_icon@3x.png */, + F0B543091F70FF8F00D29293 /* back_icon@4x.png */, F083BB241E7009EC00A9B29C /* bubbles_bg_landscape.png */, F083BB251E7009EC00A9B29C /* bubbles_bg_landscape@2x.png */, F083BB261E7009EC00A9B29C /* bubbles_bg_landscape@3x.png */, @@ -2223,6 +2238,7 @@ F083BCD91E7009EC00A9B29C /* RoomInputToolbarView.h */, F083BCDA1E7009EC00A9B29C /* RoomInputToolbarView.m */, F083BCDB1E7009EC00A9B29C /* RoomInputToolbarView.xib */, + 83711A7B1F6F8E7D008F0D4D /* KeyboardGrowingTextView.m */, ); path = RoomInputToolbar; sourceTree = ""; @@ -2819,6 +2835,7 @@ F083BE771E7009ED00A9B29C /* RoomOutgoingTextMsgWithPaginationTitleBubbleCell.xib in Resources */, F083BDA71E7009ED00A9B29C /* notificationsOff.png in Resources */, F06CDD6A1EF01E3900870B75 /* RoomEmptyBubbleCell.xib in Resources */, + F0B5430A1F70FF8F00D29293 /* back_icon@4x.png in Resources */, F083BDBD1E7009ED00A9B29C /* riot_icon@2x.png in Resources */, F083BDDE1E7009ED00A9B29C /* upload_icon@2x.png in Resources */, F083BE8B1E7009ED00A9B29C /* ExpandedRoomTitleView.xib in Resources */, @@ -2932,68 +2949,68 @@ ); inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-Riot/Pods-Riot-resources.sh", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Controllers/MXKAccountDetailsViewController.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Controllers/MXKAttachmentsViewController.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Controllers/MXKAuthenticationViewController.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Controllers/MXKCallViewController.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Controllers/MXKContactDetailsViewController.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Controllers/MXKContactListViewController.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Controllers/MXKCountryPickerViewController.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Controllers/MXKLanguagePickerViewController.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Controllers/MXKRecentListViewController.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Controllers/MXKRoomMemberDetailsViewController.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Controllers/MXKRoomMemberListViewController.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Controllers/MXKRoomSettingsViewController.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Controllers/MXKRoomViewController.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Controllers/MXKSearchViewController.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/Account/MXKAccountTableViewCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/Authentication/MXKAuthInputsEmailCodeBasedView.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/Authentication/MXKAuthInputsPasswordBasedView.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/Contact/MXKContactTableCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/DeviceView/MXKDeviceView.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/EncryptionInfoView/MXKEncryptionInfoView.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/MXKCollectionViewCell/MXKMediaCollectionViewCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/MXKEventDetailsView.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/MXKPieChartHUD.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/MXKRoomCreationView.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithButton.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndButton.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndImageView.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndMXKImageView.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndSlider.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndSubLabel.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndSwitch.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndTextField.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelTextFieldAndButton.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithPicker.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithSearchBar.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithTextFieldAndButton.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithTextView.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/PushRule/MXKPushRuleCreationTableViewCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/PushRule/MXKPushRuleTableViewCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/ReadReceipts/MXKReadReceiptTableViewCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomBubbleList/MXKRoomEmptyBubbleTableViewCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomBubbleList/MXKRoomIncomingAttachmentBubbleCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomBubbleList/MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomBubbleList/MXKRoomIncomingTextMsgBubbleCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomBubbleList/MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomBubbleList/MXKRoomIOSOutgoingBubbleTableViewCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomBubbleList/MXKRoomOutgoingAttachmentBubbleCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomBubbleList/MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomBubbleList/MXKRoomOutgoingTextMsgBubbleCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomBubbleList/MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarView.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarViewWithHPGrowingText.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarViewWithSimpleTextView.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomList/MXKInterleavedRecentTableViewCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomList/MXKPublicRoomTableViewCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomList/MXKRecentTableViewCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomMemberList/MXKRoomMemberTableViewCell.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomTitle/MXKRoomTitleView.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/RoomTitle/MXKRoomTitleViewWithTopic.xib", - "${PODS_ROOT}/../../matrix-ios-kit/MatrixKit/Views/Search/MXKSearchTableViewCell.xib", - "$PODS_CONFIGURATION_BUILD_DIR/MatrixKit/MatrixKit.bundle", - "${PODS_ROOT}/../../matrix-ios-sdk/MatrixSDK/Data/Store/MXCoreDataStore/MXCoreDataStore.xcdatamodeld", + "${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKAccountDetailsViewController.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKAttachmentsViewController.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKAuthenticationViewController.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKCallViewController.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKContactDetailsViewController.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKContactListViewController.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKCountryPickerViewController.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKLanguagePickerViewController.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKRecentListViewController.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKRoomMemberDetailsViewController.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKRoomMemberListViewController.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKRoomSettingsViewController.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKRoomViewController.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKSearchViewController.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/Account/MXKAccountTableViewCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/Authentication/MXKAuthInputsEmailCodeBasedView.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/Authentication/MXKAuthInputsPasswordBasedView.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/Contact/MXKContactTableCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/DeviceView/MXKDeviceView.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/EncryptionInfoView/MXKEncryptionInfoView.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKCollectionViewCell/MXKMediaCollectionViewCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKEventDetailsView.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKPieChartHUD.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKRoomCreationView.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithButton.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndButton.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndImageView.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndMXKImageView.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndSlider.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndSubLabel.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndSwitch.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndTextField.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelTextFieldAndButton.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithPicker.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithSearchBar.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithTextFieldAndButton.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithTextView.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/PushRule/MXKPushRuleCreationTableViewCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/PushRule/MXKPushRuleTableViewCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/ReadReceipts/MXKReadReceiptTableViewCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomEmptyBubbleTableViewCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomIncomingAttachmentBubbleCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomIncomingTextMsgBubbleCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomIOSOutgoingBubbleTableViewCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomOutgoingAttachmentBubbleCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomOutgoingTextMsgBubbleCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarView.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarViewWithHPGrowingText.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarViewWithSimpleTextView.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomList/MXKInterleavedRecentTableViewCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomList/MXKPublicRoomTableViewCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomList/MXKRecentTableViewCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomMemberList/MXKRoomMemberTableViewCell.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomTitle/MXKRoomTitleView.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomTitle/MXKRoomTitleViewWithTopic.xib", + "${PODS_ROOT}/MatrixKit/MatrixKit/Views/Search/MXKSearchTableViewCell.xib", + $PODS_CONFIGURATION_BUILD_DIR/MatrixKit/MatrixKit.bundle, + "${PODS_ROOT}/MatrixSDK/MatrixSDK/Data/Store/MXCoreDataStore/MXCoreDataStore.xcdatamodeld", ); name = "[CP] Copy Pods Resources"; outputPaths = ( @@ -3099,6 +3116,7 @@ 32471CDC1F1373A100BDF50A /* RoomMembershipCollapsedWithPaginationTitleBubbleCell.m in Sources */, F083BE2B1E7009ED00A9B29C /* AuthInputsView.m in Sources */, 321082B21F0E9F40002E0091 /* RoomMembershipCollapsedBubbleCell.m in Sources */, + 32C2356F1F7B871800E38FC5 /* WidgetPickerViewController.m in Sources */, F083BE661E7009ED00A9B29C /* RoomIncomingTextMsgWithPaginationTitleBubbleCell.m in Sources */, F083BE141E7009ED00A9B29C /* HomeViewController.m in Sources */, F083BDFB1E7009ED00A9B29C /* RoomSearchDataSource.m in Sources */, @@ -3125,6 +3143,7 @@ 3205ED7D1E976C8A003D65FA /* DirectoryServerPickerViewController.m in Sources */, F083BE7C1E7009ED00A9B29C /* DirectoryRecentTableViewCell.m in Sources */, F0E05A031E963103004B83FB /* RoomsViewController.m in Sources */, + 83711A7C1F6F8E7D008F0D4D /* KeyboardGrowingTextView.m in Sources */, F083BE801E7009ED00A9B29C /* PublicRoomTableViewCell.m in Sources */, 322806A01F0F64C4008C53D7 /* RoomMembershipExpandedBubbleCell.m in Sources */, F083BE031E7009ED00A9B29C /* EventFormatter.m in Sources */, @@ -3135,6 +3154,7 @@ F083BE6E1E7009ED00A9B29C /* RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m in Sources */, 3233F7311F31F4BF006ACA81 /* JitsiViewController.m in Sources */, F083BE2A1E7009ED00A9B29C /* UsersDevicesViewController.m in Sources */, + 32F3AE1A1F6FF4E600F0F004 /* WidgetViewController.m in Sources */, F0D2ADA11F6AA5FD00A7097D /* MXRoomSummary+Riot.m in Sources */, F083BE2D1E7009ED00A9B29C /* ForgotPasswordInputsView.m in Sources */, F083BE7E1E7009ED00A9B29C /* InviteRecentTableViewCell.m in Sources */, diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 54f72cec1..8654776c4 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -516,6 +516,12 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN [self refreshLocalContacts]; _isAppForeground = YES; + + if (@available(iOS 11.0, *)) + { + // Riot has its own dark theme. Prevent iOS from applying its one + [[UIApplication sharedApplication] keyWindow].accessibilityIgnoresInvertColors = YES; + } [self handleLaunchAnimation]; } diff --git a/Riot/Assets/Images/back_icon.png b/Riot/Assets/Images/back_icon.png index f7f8bdd24..e922a69d1 100644 Binary files a/Riot/Assets/Images/back_icon.png and b/Riot/Assets/Images/back_icon.png differ diff --git a/Riot/Assets/Images/back_icon@2x.png b/Riot/Assets/Images/back_icon@2x.png index 9ec1e9683..e996da664 100644 Binary files a/Riot/Assets/Images/back_icon@2x.png and b/Riot/Assets/Images/back_icon@2x.png differ diff --git a/Riot/Assets/Images/back_icon@3x.png b/Riot/Assets/Images/back_icon@3x.png index 83a500f10..32cdf4b60 100644 Binary files a/Riot/Assets/Images/back_icon@3x.png and b/Riot/Assets/Images/back_icon@3x.png differ diff --git a/Riot/Assets/Images/back_icon@4x.png b/Riot/Assets/Images/back_icon@4x.png new file mode 100644 index 000000000..7e4d8d484 Binary files /dev/null and b/Riot/Assets/Images/back_icon@4x.png differ diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index 10240ccd3..6b8df2779 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -416,9 +416,18 @@ "auth_email_not_found" = "Fehler beim Senden der E-Mail: Die E-Mail-Adresse wurde nicht gefunden"; "settings_user_interface" = "BENUTZEROBERFLÄCHE"; "settings_ui_language" = "Sprache"; -"settings_ui_light_theme" = "Helles Design"; -"settings_ui_dark_theme" = "Dunkles Design"; // Events formatter "event_formatter_member_updates" = "%tu Änderungen der Mitgliedschaft"; "contacts_user_directory_section" = "NUTZER VERZEICHNIS"; "contacts_user_directory_offline_section" = "NUTZER VERZEICHNIS (offline)"; +"auth_home_server_placeholder" = "URL (z.B. https://matrix.org)"; +"auth_identity_server_placeholder" = "URL (z.B. https://matrix.org)"; +"room_ongoing_conference_call_close" = "Schließen"; +"room_conference_call_no_power" = "Du brauchst die Berechtigung Konferenzgespräche in diesem Raum zu verwalten"; +"settings_labs_create_conference_with_jitsi" = "Erstelle Konferenzgespräche mit Jitsi"; +"call_already_displayed" = "Es existiert bereits ein Gespräch."; +"call_jitsi_error" = "Konferenzgespräch konnte nicht betreten werden."; +// Widget +"widget_no_power_to_manage" = "Du brauchst die Berechtigung um Widgets in diesem Raum zu verwalten"; +"widget_creation_failure" = "Widget-Erstellung fehlgeschlagen"; +"room_ongoing_conference_call_with_close" = "Laufendes Konferenzgespräch. Trete mit %@ oder %@ bei. %@ es."; diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 6a0650869..cccc4c357 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -340,8 +340,12 @@ //"settings_call_invitations" = "Call invitations"; "settings_ui_language" = "Language"; -"settings_ui_light_theme" = "Light theme"; -"settings_ui_dark_theme" = "Dark theme"; +"settings_ui_theme" = "Theme"; +"settings_ui_theme_auto" = "Auto"; +"settings_ui_theme_light" = "Light"; +"settings_ui_theme_dark" = "Dark"; +"settings_ui_theme_picker_title" = "Select a theme"; +"settings_ui_theme_picker_message" = "\"Auto\" uses your device \"Invert Colours\" settings"; "settings_unignore_user" = "Show all messages from %@?"; @@ -389,6 +393,7 @@ "room_details_favourite_tag" = "Favourite"; "room_details_low_priority_tag" = "Low priority"; "room_details_mute_notifs" = "Mute notifications"; +"room_details_direct_chat" = "Direct Chat"; "room_details_access_section"="Who can access this room?"; "room_details_access_section_invited_only"="Only people who have been invited"; "room_details_access_section_anyone_apart_from_guest"="Anyone who knows the room's link, apart from guests"; @@ -428,6 +433,7 @@ "room_details_fail_to_add_room_aliases" = "Fail to add the new room addresses"; "room_details_fail_to_remove_room_aliases" = "Fail to remove the room addresses"; "room_details_fail_to_update_room_canonical_alias" = "Fail to update the main address"; +"room_details_fail_to_update_room_direct" = "Fail to update the direct flag of this room"; "room_details_fail_to_enable_encryption" = "Fail to enable encryption in this room"; "room_details_save_changes_prompt" = "Do you want to save changes?"; "room_details_set_main_address" = "Set as Main Address"; diff --git a/Riot/Assets/es.lproj/Vector.strings b/Riot/Assets/es.lproj/Vector.strings index 20cb49b36..f6c67d3bc 100644 --- a/Riot/Assets/es.lproj/Vector.strings +++ b/Riot/Assets/es.lproj/Vector.strings @@ -2,3 +2,45 @@ "title_home" = "Inicio"; "title_favourites" = "Favoritos"; "title_people" = "Contactos"; +"title_rooms" = "Salas"; +"warning" = "Atención"; +// Actions +"view" = "Ver"; +"next" = "SIguiente"; +"back" = "Anterior"; +"continue" = "Continuar"; +"create" = "Crear"; +"start" = "Iniciar"; +"leave" = "Salir"; +"remove" = "Remover"; +"invite" = "Invitar"; +"retry" = "Re-intentar"; +"on" = "Activado"; +"off" = "Desactivado"; +"cancel" = "Cancelar"; +"save" = "Guardar"; +"join" = "Entrar"; +"decline" = "Rechazar"; +"accept" = "Aceptar"; +"preview" = "Vista previa"; +"camera" = "Cámara"; +"voice" = "Voz"; +"video" = "Vídeo"; +"active_call" = "Llamada activa"; +"active_call_details" = "Llamada activa (%@)"; +"later" = "Después"; +"rename" = "Renombrar"; +"collapse" = "colapsar"; +// Authentication +"auth_login" = "Ingresar"; +"auth_register" = "Registrar"; +"auth_submit" = "Enviar"; +"auth_skip" = "Omitir"; +"auth_send_reset_email" = "Enviar Email de restauración"; +"auth_return_to_login" = "Regresar a pantalla de ingreso"; +"auth_user_id_placeholder" = "Email o nombre de usuario"; +"auth_password_placeholder" = "Contraseña"; +"auth_new_password_placeholder" = "Nueva contraseña"; +"auth_user_name_placeholder" = "Nombre de usuario"; +"auth_optional_email_placeholder" = "Dirección de Email (opcional)"; +"auth_email_placeholder" = "Dirección Email"; diff --git a/Riot/Assets/eu.lproj/Vector.strings b/Riot/Assets/eu.lproj/Vector.strings index 17830aa3d..1eed34a84 100644 --- a/Riot/Assets/eu.lproj/Vector.strings +++ b/Riot/Assets/eu.lproj/Vector.strings @@ -422,3 +422,14 @@ "bug_report_progress_zipping" = "Egunkariak biltzen"; "bug_report_progress_uploading" = "Txostena igotzen"; "room_details_advanced_e2e_encryption_prompt_message" = "Muturretik muturrerako zifratzea esperimentala da eta agian ez dabil behar bezala.\n\nEz zenuke datuak babesteko erabili behar oraindik.\n\nGailuek ezin izango dute gelara elakrtu aurreko historiala deszifratu.\n\nBehin gela batean zifratzea aktibatuta ez dago gero desaktibatzerik (oraingoz).\n\nZifratutako mezuak ezin izango dira ikusi oraindik zifratzea onartzen ez duten bezeroetan."; +"auth_home_server_placeholder" = "URL (adib. https://matrix.org)"; +"auth_identity_server_placeholder" = "URL (adib. https://matrix.org)"; +"room_ongoing_conference_call_with_close" = "Konferentzia deia abian. elkartu %@ edo %@ gisa. %@."; +"room_ongoing_conference_call_close" = "Itxi"; +"room_conference_call_no_power" = "Baimena behar duzu konferentzia deia kudeatzeko gela honetan"; +"settings_labs_create_conference_with_jitsi" = "Sortu konferentzia deia jitsi erabiliz"; +"call_already_displayed" = "Badago de bat abian."; +"call_jitsi_error" = "Hutsegitea konferentzia deia elkartzean."; +// Widget +"widget_no_power_to_manage" = "Baimena behar duzu trepetak kudeatzeko gela honetan"; +"widget_creation_failure" = "Trepetaren sorrerak huts egin du"; diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index 8bf60b617..06bec44d4 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -313,6 +313,7 @@ "room_details_favourite_tag" = "Favoris"; "room_details_low_priority_tag" = "Priorité basse"; "room_details_mute_notifs" = "Désactiver les notifications"; +"room_details_direct_chat" = "Discussion directe"; "room_details_access_section" = "Qui peut accéder à ce salon ?"; "room_details_access_section_invited_only" = "Seulement les personnes qui ont été invitées"; "room_details_access_section_anyone_apart_from_guest" = "N'importe qui ayant un lien vers le salon, hormis les visiteurs"; @@ -352,6 +353,7 @@ "room_details_fail_to_add_room_aliases" = "Impossible d'ajouter les nouvelles adresses au salon"; "room_details_fail_to_remove_room_aliases" = "Impossible de supprimer les adresses du salon"; "room_details_fail_to_update_room_canonical_alias" = "Impossible de modifier l'adresse principale"; +"room_details_fail_to_update_room_direct" = "Impossible de modifier l'état de discussion directe"; "room_details_fail_to_enable_encryption" = "Impossible d'activer le chiffrement sur ce salon"; "room_details_save_changes_prompt" = "Voulez-vous enregistrer les changements ?"; "room_details_set_main_address" = "Configurer comme adresse principale"; @@ -415,8 +417,12 @@ "auth_phone_in_use" = "Ce numéro de téléphone est déjà utilisé"; "auth_email_not_found" = "Échec lors de l'envoi de l'e-mail : Cette adresse e-mail n'a pas pu être trouvée"; "settings_ui_language" = "Langue"; -"settings_ui_light_theme" = "Thème clair"; -"settings_ui_dark_theme" = "Thème sombre"; +"settings_ui_theme" = "Thème"; +"settings_ui_theme_auto" = "Auto"; +"settings_ui_theme_light" = "Clair"; +"settings_ui_theme_dark" = "Sombre"; +"settings_ui_theme_picker_title" = "Selectionnez un thème"; +"settings_ui_theme_picker_message" = "\"Auto\" utilise le paramètre \"Inverser les couleurs\" de votre appareil"; "collapse" = "réduire"; "auth_untrusted_id_server" = "Le serveur d'identité n'est pas fiable"; "settings_user_interface" = "INTERFACE UTILISATEUR"; diff --git a/Riot/Assets/nl.lproj/Vector.strings b/Riot/Assets/nl.lproj/Vector.strings index cb160fb24..6290d9b57 100644 --- a/Riot/Assets/nl.lproj/Vector.strings +++ b/Riot/Assets/nl.lproj/Vector.strings @@ -440,11 +440,20 @@ "contacts_user_directory_offline_section" = "GEBRUIKERSADRESBOEK (offline)"; "settings_user_interface" = "GEBRUIKERSINTERFACE"; "settings_ui_language" = "Taal"; -"settings_ui_light_theme" = "Lichte thema"; -"settings_ui_dark_theme" = "Donkere thema"; // Read Receipts "read_receipts_list" = "Leesbewijzen Lijst"; "receipt_status_read" = "Lees: "; // Events formatter "event_formatter_member_updates" = "%tu lidmaatschap aanpassingen"; "bug_report_send" = "Stuur"; +"auth_home_server_placeholder" = "URL (bv. https://matrix.org)"; +"auth_identity_server_placeholder" = "URL (bv. https://matrix.org)"; +"room_ongoing_conference_call_with_close" = "Lopend vergadergesprek. Neem deel als %@ of %@. %@ het."; +"room_ongoing_conference_call_close" = "Sluiten"; +"room_conference_call_no_power" = "Je hebt permissie nodig om het vergadergesprek in deze ruimte te beheren"; +"settings_labs_create_conference_with_jitsi" = "Maak vergadergesprekken met jitsi"; +"call_already_displayed" = "Er is al een gesprek aan de gang."; +"call_jitsi_error" = "Het is niet gelukt om aan het vergadergesprek deel te nemen."; +// Widget +"widget_no_power_to_manage" = "Je hebt permissie nodig om widgets in deze ruimte te beheren"; +"widget_creation_failure" = "Het creëren van de widget is fout gegaan"; diff --git a/Riot/Assets/ru.lproj/InfoPlist.strings b/Riot/Assets/ru.lproj/InfoPlist.strings new file mode 100644 index 000000000..1de43791c --- /dev/null +++ b/Riot/Assets/ru.lproj/InfoPlist.strings @@ -0,0 +1,5 @@ +// Permissions usage explanations +"NSCameraUsageDescription" = "Камера используется для съемки фотографий и видеороликов, а также для видеозвонков."; +"NSPhotoLibraryUsageDescription" = "Галерея используется для отправки фотографий и видео."; +"NSMicrophoneUsageDescription" = "Микрофон используется при съемке видео и выполнении звонков."; +"NSContactsUsageDescription" = "Контакты используются для поиска пользователей в Riot по электронной почте или номеру телефона."; diff --git a/Riot/Assets/ru.lproj/Localizable.strings b/Riot/Assets/ru.lproj/Localizable.strings index 09bc3ceba..a93cb96f6 100644 --- a/Riot/Assets/ru.lproj/Localizable.strings +++ b/Riot/Assets/ru.lproj/Localizable.strings @@ -41,7 +41,7 @@ /* Incoming unnamed voice conference invite from a specific person */ "VOICE_CONF_FROM_USER" = "Групповой звонок от %@"; /* Incoming unnamed video conference invite from a specific person */ -"VIDEO_CONF_FROM_USER" = "Видеоконференция %@"; +"VIDEO_CONF_FROM_USER" = "Групповой видеозвонок от %@"; /* Incoming named voice conference invite from a specific person */ "VOICE_CONF_NAMED_FROM_USER" = "Групповой звонок от %@: '%@'"; /* Incoming named video conference invite from a specific person */ diff --git a/Riot/Assets/ru.lproj/Vector.strings b/Riot/Assets/ru.lproj/Vector.strings index 33e345610..b4d02c15f 100644 --- a/Riot/Assets/ru.lproj/Vector.strings +++ b/Riot/Assets/ru.lproj/Vector.strings @@ -262,8 +262,6 @@ "settings_global_settings_info" = "Глобальные настройки уведомлений доступны в вашем %@ веб-клиенте"; "settings_on_denied_notification" = "Уведомления для %@ запрещены, пожалуйста, разрешите их в настройках вашего устройства"; "settings_ui_language" = "Язык"; -"settings_ui_light_theme" = "Светлая тема"; -"settings_ui_dark_theme" = "Темная тема"; "settings_unignore_user" = "Показать все сообщения от %@?"; "settings_labs_e2e_encryption" = "Сквозное шифрование"; "settings_labs_e2e_encryption_prompt_message" = "Чтобы завершить настройку шифрования, вы должны войти в систему еще раз."; @@ -421,3 +419,15 @@ "settings_contacts_phonebook_country" = "Страна телефонной книги"; "no_voip" = "%@ вызывает вас, но %@ пока не поддерживает вызовы.\nМожно пропустить это уведомление и ответить на звонок с другого устройства, или вы можете отклонить его."; "room_participants_remove_third_party_invite_msg" = "Удалить приглашение стороннего сервера невозможно, пока не описан API"; +"auth_home_server_placeholder" = "URL (например https://matrix.org)"; +"auth_identity_server_placeholder" = "URL (например https://matrix.org)"; +"room_ongoing_conference_call_with_close" = "Текущий групповой звонок. Войдите как %@ или %@. %@."; +"room_ongoing_conference_call_close" = "Закрыть"; +"room_conference_call_no_power" = "Вам нужно разрешение на управление конференцией в этой комнате"; +"room_preview_unlinked_email_warning" = "Это приглашение было отправлено %@, и оно не связано с этой учетной записью. Возможно, вы пожелаете войти в систему с другой учетной записью или добавить это сообщение к вашей учетной записи."; +"settings_labs_create_conference_with_jitsi" = "Создать групповой вызов с jitsi"; +"call_already_displayed" = "Вызов уже установлен."; +"call_jitsi_error" = "Не удалось присоединиться к групповому вызову."; +// Widget +"widget_no_power_to_manage" = "Вам нужно разрешение на управление виджетами в этой комнате"; +"widget_creation_failure" = "Не удалось создать виджет"; diff --git a/Riot/Assets/zh_Hans.lproj/Vector.strings b/Riot/Assets/zh_Hans.lproj/Vector.strings index 304310339..27f99e6a9 100644 --- a/Riot/Assets/zh_Hans.lproj/Vector.strings +++ b/Riot/Assets/zh_Hans.lproj/Vector.strings @@ -421,3 +421,9 @@ "settings_ui_dark_theme" = "暗色主题"; // Events formatter "event_formatter_member_updates" = "%tu 的成员身份变化"; +"auth_home_server_placeholder" = "URL(例如 https://matrix.org)"; +"auth_identity_server_placeholder" = "URL(例如 https://matrix.org)"; +"contacts_user_directory_section" = "用户目录"; +"contacts_user_directory_offline_section" = "用户目录(离线)"; +"room_ongoing_conference_call_with_close" = "收到会议通话。以 %@ 或 %@.%@ 加入。"; +"room_ongoing_conference_call_close" = "关闭"; diff --git a/Riot/Riot-Defaults.plist b/Riot/Riot-Defaults.plist index 7b9df11ce..da87bc19d 100644 --- a/Riot/Riot-Defaults.plist +++ b/Riot/Riot-Defaults.plist @@ -25,9 +25,9 @@ webAppUrl https://riot.im/app integrationsUiUrl - https://scalar.vector.im/ + https://scalar-staging.riot.im/scalar-web/ integrationsRestUrl - https://scalar.vector.im/api + https://scalar-staging.riot.im/scalar/api apnsDeviceToken showAllEventsInRoomHistory diff --git a/Riot/Utils/RiotDesignValues.m b/Riot/Utils/RiotDesignValues.m index cd0ee8756..d52782d32 100644 --- a/Riot/Utils/RiotDesignValues.m +++ b/Riot/Utils/RiotDesignValues.m @@ -112,8 +112,10 @@ UIColor *kRiotDesignSearchBarTintColor = nil; // Observe user interface theme change. [[NSUserDefaults standardUserDefaults] addObserver:[RiotDesignValues sharedInstance] forKeyPath:@"userInterfaceTheme" options:0 context:nil]; [[RiotDesignValues sharedInstance] userInterfaceThemeDidChange]; -} + // Observe "Invert Colours" settings changes (available since iOS 11) + [[NSNotificationCenter defaultCenter] addObserver:[RiotDesignValues sharedInstance] selector:@selector(accessibilityInvertColorsStatusDidChange) name:UIAccessibilityInvertColorsStatusDidChangeNotification object:nil]; +} - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { @@ -123,10 +125,25 @@ UIColor *kRiotDesignSearchBarTintColor = nil; } } +- (void)accessibilityInvertColorsStatusDidChange +{ + // Refresh the theme only for "auto" + NSString *theme = [[NSUserDefaults standardUserDefaults] stringForKey:@"userInterfaceTheme"]; + if (!theme || [theme isEqualToString:@"auto"]) + { + [self userInterfaceThemeDidChange]; + } +} + - (void)userInterfaceThemeDidChange { - // Retrieve the current selected theme ("light" if none). + // Retrieve the current selected theme ("light" if none. "auto" is used as default from iOS 11). NSString *theme = [[NSUserDefaults standardUserDefaults] stringForKey:@"userInterfaceTheme"]; + + if (!theme || [theme isEqualToString:@"auto"]) + { + theme = UIAccessibilityIsInvertColorsEnabled() ? @"dark" : @"light"; + } // Currently only 2 themes is supported if ([theme isEqualToString:@"dark"]) diff --git a/Riot/Utils/Widgets/Widget.m b/Riot/Utils/Widgets/Widget.m index 381e23c48..93ddd73a5 100644 --- a/Riot/Utils/Widgets/Widget.m +++ b/Riot/Utils/Widgets/Widget.m @@ -41,11 +41,26 @@ MXJSONModelSetDictionary(_data, widgetEvent.content[@"data"]); // Format the url string with user data - _url = [_url stringByReplacingOccurrencesOfString:@"$matrix_user_id" withString:mxSession.myUser.userId]; - _url = [_url stringByReplacingOccurrencesOfString:@"$matrix_display_name" - withString:mxSession.myUser.displayname ? mxSession.myUser.displayname : mxSession.myUser.userId]; - _url = [_url stringByReplacingOccurrencesOfString:@"$matrix_avatar_url" - withString:mxSession.myUser.avatarUrl ? mxSession.myUser.avatarUrl : @""]; + if (_url) + { + _url = [_url stringByReplacingOccurrencesOfString:@"$matrix_user_id" withString:mxSession.myUser.userId]; + _url = [_url stringByReplacingOccurrencesOfString:@"$matrix_display_name" + withString:mxSession.myUser.displayname ? mxSession.myUser.displayname : mxSession.myUser.userId]; + _url = [_url stringByReplacingOccurrencesOfString:@"$matrix_avatar_url" + withString:mxSession.myUser.avatarUrl ? mxSession.myUser.avatarUrl : @""]; + + // And their scalar token + NSString *scalarToken = [[WidgetManager sharedManager] scalarTokenForMXSession:mxSession]; + if (scalarToken) + { + _url = [_url stringByAppendingString:[NSString stringWithFormat:@"&scalar_token=%@", scalarToken]]; + } + else + { + // Some widget can live without scalar token (ex: Jitsi widget) + NSLog(@"[Widget] Note: There is no scalar token for %@", self); + } + } } return self; diff --git a/Riot/Utils/Widgets/WidgetManager.h b/Riot/Utils/Widgets/WidgetManager.h index 77b78340b..f717f96df 100644 --- a/Riot/Utils/Widgets/WidgetManager.h +++ b/Riot/Utils/Widgets/WidgetManager.h @@ -72,12 +72,20 @@ WidgetManagerErrorCode; /** List all active widgets of a given type in a room. - @param widgetType the types of widget to search. + @param widgetTypes the types of widget to search. Nil means all types. @param room the room to check. @return a list of widgets. */ - (NSArray *)widgetsOfTypes:(NSArray*)widgetTypes inRoom:(MXRoom*)room; +/** + List all active widgets of a given type in a room, excluding some types. + + @param notWidgetTypes the types of widget to not consider. Nil means all types. + @param room the room to check. + @return a list of widgets. + */ +- (NSArray *)widgetsNotOfTypes:(NSArray*)notWidgetTypes inRoom:(MXRoom*)room; /** Add a modular widget to a room. diff --git a/Riot/Utils/Widgets/WidgetManager.m b/Riot/Utils/Widgets/WidgetManager.m index 169ca5c1e..80a7de1a6 100644 --- a/Riot/Utils/Widgets/WidgetManager.m +++ b/Riot/Utils/Widgets/WidgetManager.m @@ -87,7 +87,17 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; return [self widgetsOfTypes:nil inRoom:room]; } -- (NSArray *)widgetsOfTypes:(NSArray *)widgetTypes inRoom:(MXRoom *)room +- (NSArray *)widgetsOfTypes:(NSArray*)widgetTypes inRoom:(MXRoom*)room; +{ + return [self widgetsOfTypes:widgetTypes butNotTypesOf:nil inRoom:room]; +} + +- (NSArray *)widgetsNotOfTypes:(NSArray*)notWidgetTypes inRoom:(MXRoom*)room +{ + return [self widgetsOfTypes:nil butNotTypesOf:notWidgetTypes inRoom:room]; +} + +- (NSArray *)widgetsOfTypes:(NSArray*)widgetTypes butNotTypesOf:(NSArray*)notWidgetTypes inRoom:(MXRoom*)room; { // Widget id -> widget NSMutableDictionary *widgets = [NSMutableDictionary dictionary]; @@ -118,14 +128,21 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; for (MXEvent *widgetEvent in widgetEvents) { // Filter widget types if required - if (widgetTypes) + if (widgetTypes || notWidgetTypes) { NSString *widgetType; MXJSONModelSetString(widgetType, widgetEvent.content[@"type"]); - if (widgetType && NSNotFound == [widgetTypes indexOfObject:widgetType]) + if (widgetType) { - continue; + if (widgetTypes && NSNotFound == [widgetTypes indexOfObject:widgetType]) + { + continue; + } + if (notWidgetTypes && NSNotFound != [notWidgetTypes indexOfObject:widgetType]) + { + continue; + } } } diff --git a/Riot/ViewController/AttachmentsViewController.m b/Riot/ViewController/AttachmentsViewController.m index 934945eb8..3fd4dec11 100644 --- a/Riot/ViewController/AttachmentsViewController.m +++ b/Riot/ViewController/AttachmentsViewController.m @@ -62,6 +62,10 @@ self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; self.activityIndicator.backgroundColor = kRiotOverlayColor; + + self.navigationBar.tintColor = kRiotSecondaryBgColor; + self.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName: kRiotPrimaryTextColor}; + self.backButton.tintColor = kRiotColorGreen; } - (void)viewWillAppear:(BOOL)animated diff --git a/Riot/ViewController/BugReportViewController.m b/Riot/ViewController/BugReportViewController.m index cc0a332d6..92ec61141 100644 --- a/Riot/ViewController/BugReportViewController.m +++ b/Riot/ViewController/BugReportViewController.m @@ -89,9 +89,6 @@ { _titleLabel.text = NSLocalizedStringFromTable(@"bug_report_title", @"Vector", nil); _descriptionLabel.text = NSLocalizedStringFromTable(@"bug_report_description", @"Vector", nil); - - // Allow to send empty description for crash report but not for bug report - _sendButton.enabled = NO; } [_cancelButton setTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] forState:UIControlStateNormal]; diff --git a/Riot/ViewController/ContactDetailsViewController.m b/Riot/ViewController/ContactDetailsViewController.m index fa97fbe79..1b69d4a10 100644 --- a/Riot/ViewController/ContactDetailsViewController.m +++ b/Riot/ViewController/ContactDetailsViewController.m @@ -35,7 +35,7 @@ #define TABLEVIEW_SECTION_HEADER_HEIGHT 28 #define TABLEVIEW_SECTION_HEADER_HEIGHT_WHEN_HIDDEN 0.01f -@interface ContactDetailsViewController () +@interface ContactDetailsViewController () { RoomMemberTitleView* contactTitleView; MXKImageView *contactAvatar; @@ -132,19 +132,62 @@ actionsArray = [[NSMutableArray alloc] init]; directChatsArray = [[NSMutableArray alloc] init]; + contactTitleView = [RoomMemberTitleView roomMemberTitleView]; + contactTitleView.delegate = self; + contactAvatar = contactTitleView.memberAvatar; + contactAvatar.contentMode = UIViewContentModeScaleAspectFill; + contactAvatar.defaultBackgroundColor = [UIColor clearColor]; + + if (@available(iOS 11.0, *)) + { + // Define directly the navigation titleView with the custom title view instance. Do not use anymore a container. + self.navigationItem.titleView = contactTitleView; + } + else + { + self.navigationItem.titleView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 600, 40)]; + + // Add the title view and define edge constraints + contactTitleView.translatesAutoresizingMaskIntoConstraints = NO; + [self.navigationItem.titleView addSubview:contactTitleView]; + + NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:contactTitleView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.navigationItem.titleView + attribute:NSLayoutAttributeTop + multiplier:1.0f + constant:0.0f]; + NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:contactTitleView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self.navigationItem.titleView + attribute:NSLayoutAttributeBottom + multiplier:1.0f + constant:0.0f]; + NSLayoutConstraint *leadingConstraint = [NSLayoutConstraint constraintWithItem:contactTitleView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self.navigationItem.titleView + attribute:NSLayoutAttributeLeading + multiplier:1.0f + constant:0.0f]; + NSLayoutConstraint *trailingConstraint = [NSLayoutConstraint constraintWithItem:contactTitleView + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.navigationItem.titleView + attribute:NSLayoutAttributeTrailing + multiplier:1.0f + constant:0.0f]; + [NSLayoutConstraint activateConstraints:@[topConstraint, bottomConstraint, leadingConstraint, trailingConstraint]]; + } + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; [tap setNumberOfTouchesRequired:1]; [tap setNumberOfTapsRequired:1]; [tap setDelegate:self]; [self.contactNameLabelMask addGestureRecognizer:tap]; self.contactNameLabelMask.userInteractionEnabled = YES; - - self.navigationItem.titleView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 600, 40)]; - - contactTitleView = [RoomMemberTitleView roomMemberTitleView]; - contactAvatar = contactTitleView.memberAvatar; - contactAvatar.contentMode = UIViewContentModeScaleAspectFill; - contactAvatar.defaultBackgroundColor = [UIColor clearColor]; // Add tap to show the contact avatar in fullscreen tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; @@ -162,40 +205,6 @@ [tap setDelegate:self]; [self.contactAvatarMask addGestureRecognizer:tap]; self.contactAvatarMask.userInteractionEnabled = YES; - - // Add the title view and define edge constraints - contactTitleView.translatesAutoresizingMaskIntoConstraints = NO; - [self.navigationItem.titleView addSubview:contactTitleView]; - - NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:contactTitleView - attribute:NSLayoutAttributeTop - relatedBy:NSLayoutRelationEqual - toItem:self.navigationItem.titleView - attribute:NSLayoutAttributeTop - multiplier:1.0f - constant:0.0f]; - NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:contactTitleView - attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual - toItem:self.navigationItem.titleView - attribute:NSLayoutAttributeBottom - multiplier:1.0f - constant:0.0f]; - NSLayoutConstraint *leadingConstraint = [NSLayoutConstraint constraintWithItem:contactTitleView - attribute:NSLayoutAttributeLeading - relatedBy:NSLayoutRelationEqual - toItem:self.navigationItem.titleView - attribute:NSLayoutAttributeLeading - multiplier:1.0f - constant:0.0f]; - NSLayoutConstraint *trailingConstraint = [NSLayoutConstraint constraintWithItem:contactTitleView - attribute:NSLayoutAttributeTrailing - relatedBy:NSLayoutRelationEqual - toItem:self.navigationItem.titleView - attribute:NSLayoutAttributeTrailing - multiplier:1.0f - constant:0.0f]; - [NSLayoutConstraint activateConstraints:@[topConstraint, bottomConstraint, leadingConstraint, trailingConstraint]]; // Register collection view cell class [self.tableView registerClass:TableViewCellWithButton.class forCellReuseIdentifier:[TableViewCellWithButton defaultReuseIdentifier]]; @@ -354,7 +363,8 @@ { [super viewDidLayoutSubviews]; - if (contactTitleView) + // Check whether the title view has been created and rendered. + if (contactTitleView && contactTitleView.superview) { // Adjust the header height by taking into account the actual position of the member avatar in title view // This position depends automatically on the screen orientation. @@ -1177,4 +1187,11 @@ } } +#pragma mark - RoomMemberTitleViewDelegate + +- (void)roomMemberTitleViewDidLayoutSubview:(RoomMemberTitleView*)titleView +{ + [self viewDidLayoutSubviews]; +} + @end diff --git a/Riot/ViewController/ContactsTableViewController.m b/Riot/ViewController/ContactsTableViewController.m index 299bab4f8..d41be0bb5 100644 --- a/Riot/ViewController/ContactsTableViewController.m +++ b/Riot/ViewController/ContactsTableViewController.m @@ -163,7 +163,7 @@ // Observe kAppDelegateDidTapStatusBarNotification. kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - [self.contactsTableView setContentOffset:CGPointMake(-self.contactsTableView.contentInset.left, -self.contactsTableView.contentInset.top) animated:YES]; + [self.contactsTableView setContentOffset:CGPointMake(-self.contactsTableView.mxk_adjustedContentInset.left, -self.contactsTableView.mxk_adjustedContentInset.top) animated:YES]; }]; @@ -285,7 +285,7 @@ - (void)scrollToTop:(BOOL)animated { // Scroll to the top - [self.contactsTableView setContentOffset:CGPointMake(-self.contactsTableView.contentInset.left, -self.contactsTableView.contentInset.top) animated:animated]; + [self.contactsTableView setContentOffset:CGPointMake(-self.contactsTableView.mxk_adjustedContentInset.left, -self.contactsTableView.mxk_adjustedContentInset.top) animated:animated]; } #pragma mark - UITableView delegate diff --git a/Riot/ViewController/DirectoryServerPickerViewController.m b/Riot/ViewController/DirectoryServerPickerViewController.m index 6beb3bbe2..e9935f681 100644 --- a/Riot/ViewController/DirectoryServerPickerViewController.m +++ b/Riot/ViewController/DirectoryServerPickerViewController.m @@ -153,7 +153,7 @@ // Observe kAppDelegateDidTapStatusBarNotificationObserver. kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - [self.tableView setContentOffset:CGPointMake(-self.tableView.contentInset.left, -self.tableView.contentInset.top) animated:YES]; + [self.tableView setContentOffset:CGPointMake(-self.tableView.mxk_adjustedContentInset.left, -self.tableView.mxk_adjustedContentInset.top) animated:YES]; }]; diff --git a/Riot/ViewController/DirectoryViewController.m b/Riot/ViewController/DirectoryViewController.m index f75eae491..ef4883b40 100644 --- a/Riot/ViewController/DirectoryViewController.m +++ b/Riot/ViewController/DirectoryViewController.m @@ -115,7 +115,7 @@ // Observe kAppDelegateDidTapStatusBarNotificationObserver. kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - [self.tableView setContentOffset:CGPointMake(-self.tableView.contentInset.left, -self.tableView.contentInset.top) animated:YES]; + [self.tableView setContentOffset:CGPointMake(-self.tableView.mxk_adjustedContentInset.left, -self.tableView.mxk_adjustedContentInset.top) animated:YES]; }]; diff --git a/Riot/ViewController/MediaPickerViewController.m b/Riot/ViewController/MediaPickerViewController.m index 0002f8960..262ecd1c0 100644 --- a/Riot/ViewController/MediaPickerViewController.m +++ b/Riot/ViewController/MediaPickerViewController.m @@ -1000,6 +1000,12 @@ static void *RecordingContext = &RecordingContext; NSLog(@"[MediaPickerVC] Attemping to setup AVCapture when it is already started!"); return; } + if (!cameraQueue) + { + NSLog(@"[MediaPickerVC] Attemping to setup AVCapture when it is being destroyed!"); + return; + } + isCaptureSessionSetupInProgress = YES; [self.cameraActivityIndicator startAnimating]; @@ -1192,6 +1198,12 @@ static void *RecordingContext = &RecordingContext; - (void)tearDownAVCapture { + if (!cameraQueue) + { + NSLog(@"[MediaPickerVC] Attemping to tear down AVCapture when it is being destroyed!"); + return; + } + dispatch_sync(cameraQueue, ^{ frontCameraInput = nil; backCameraInput = nil; diff --git a/Riot/ViewController/RecentsViewController.m b/Riot/ViewController/RecentsViewController.m index 7a0fd765a..82f00299e 100644 --- a/Riot/ViewController/RecentsViewController.m +++ b/Riot/ViewController/RecentsViewController.m @@ -656,7 +656,7 @@ } // Look for the lowest section index visible in the bottom sticky headers. - CGFloat maxVisiblePosY = self.recentsTableView.contentOffset.y + self.recentsTableView.frame.size.height - self.recentsTableView.contentInset.bottom; + CGFloat maxVisiblePosY = self.recentsTableView.contentOffset.y + self.recentsTableView.frame.size.height - self.recentsTableView.mxk_adjustedContentInset.bottom; UIView *lastDisplayedSectionHeader = displayedSectionHeaders.lastObject; for (UIView *header in _stickyHeadersBottomContainer.subviews) @@ -1252,7 +1252,7 @@ { if (!self.recentsSearchBar.isHidden) { - if (!self.recentsSearchBar.text.length && (scrollView.contentOffset.y + scrollView.contentInset.top > self.recentsSearchBar.frame.size.height)) + if (!self.recentsSearchBar.text.length && (scrollView.contentOffset.y + scrollView.mxk_adjustedContentInset.top > self.recentsSearchBar.frame.size.height)) { // Hide the search bar [self hideSearchBar:YES]; @@ -1770,7 +1770,7 @@ - (void)scrollToTop:(BOOL)animated { - [self.recentsTableView setContentOffset:CGPointMake(-self.recentsTableView.contentInset.left, -self.recentsTableView.contentInset.top) animated:animated]; + [self.recentsTableView setContentOffset:CGPointMake(-self.recentsTableView.mxk_adjustedContentInset.left, -self.recentsTableView.mxk_adjustedContentInset.top) animated:animated]; } - (void)scrollToTheTopTheNextRoomWithMissedNotificationsInSection:(NSInteger)section diff --git a/Riot/ViewController/RoomFilesSearchViewController.m b/Riot/ViewController/RoomFilesSearchViewController.m index c324416e4..9c7b09dab 100644 --- a/Riot/ViewController/RoomFilesSearchViewController.m +++ b/Riot/ViewController/RoomFilesSearchViewController.m @@ -118,7 +118,7 @@ // Observe kAppDelegateDidTapStatusBarNotificationObserver. kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - [self.searchTableView setContentOffset:CGPointMake(-self.searchTableView.contentInset.left, -self.searchTableView.contentInset.top) animated:YES]; + [self.searchTableView setContentOffset:CGPointMake(-self.searchTableView.mxk_adjustedContentInset.left, -self.searchTableView.mxk_adjustedContentInset.top) animated:YES]; }]; } diff --git a/Riot/ViewController/RoomMemberDetailsViewController.m b/Riot/ViewController/RoomMemberDetailsViewController.m index 9aaed6b90..c0c004ca0 100644 --- a/Riot/ViewController/RoomMemberDetailsViewController.m +++ b/Riot/ViewController/RoomMemberDetailsViewController.m @@ -33,7 +33,7 @@ #define TABLEVIEW_SECTION_HEADER_HEIGHT 28 #define TABLEVIEW_SECTION_HEADER_HEIGHT_WHEN_HIDDEN 0.01f -@interface RoomMemberDetailsViewController () +@interface RoomMemberDetailsViewController () { RoomMemberTitleView* memberTitleView; @@ -118,6 +118,58 @@ [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. + memberTitleView = [RoomMemberTitleView roomMemberTitleView]; + memberTitleView.delegate = self; + + if (@available(iOS 11.0, *)) + { + // Define directly the navigation titleView with the custom title view instance. Do not use anymore a container. + self.navigationItem.titleView = memberTitleView; + } + else + { + self.navigationItem.titleView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 600, 40)]; + + // Add the title view and define edge constraints + memberTitleView.translatesAutoresizingMaskIntoConstraints = NO; + [self.navigationItem.titleView addSubview:memberTitleView]; + + NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:memberTitleView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.navigationItem.titleView + attribute:NSLayoutAttributeTop + multiplier:1.0f + constant:0.0f]; + NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:memberTitleView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self.navigationItem.titleView + attribute:NSLayoutAttributeBottom + multiplier:1.0f + constant:0.0f]; + NSLayoutConstraint *leadingConstraint = [NSLayoutConstraint constraintWithItem:memberTitleView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self.navigationItem.titleView + attribute:NSLayoutAttributeLeading + multiplier:1.0f + constant:0.0f]; + NSLayoutConstraint *trailingConstraint = [NSLayoutConstraint constraintWithItem:memberTitleView + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.navigationItem.titleView + attribute:NSLayoutAttributeTrailing + multiplier:1.0f + constant:0.0f]; + + [NSLayoutConstraint activateConstraints:@[topConstraint, bottomConstraint, leadingConstraint, trailingConstraint]]; + } + + // Handle the member avatar at the view controller level. + self.memberThumbnail = memberTitleView.memberAvatar; + + // Add tap gesture on member's name UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; [tap setNumberOfTouchesRequired:1]; [tap setNumberOfTapsRequired:1]; @@ -125,11 +177,6 @@ [self.roomMemberNameLabelMask addGestureRecognizer:tap]; self.roomMemberNameLabelMask.userInteractionEnabled = YES; - self.navigationItem.titleView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 600, 40)]; - - memberTitleView = [RoomMemberTitleView roomMemberTitleView]; - self.memberThumbnail = memberTitleView.memberAvatar; - // Add tap to show the room member avatar in fullscreen tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; [tap setNumberOfTouchesRequired:1]; @@ -146,40 +193,6 @@ [tap setDelegate:self]; [self.roomMemberAvatarMask addGestureRecognizer:tap]; self.roomMemberAvatarMask.userInteractionEnabled = YES; - - // Add the title view and define edge constraints - memberTitleView.translatesAutoresizingMaskIntoConstraints = NO; - [self.navigationItem.titleView addSubview:memberTitleView]; - - NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:memberTitleView - attribute:NSLayoutAttributeTop - relatedBy:NSLayoutRelationEqual - toItem:self.navigationItem.titleView - attribute:NSLayoutAttributeTop - multiplier:1.0f - constant:0.0f]; - NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:memberTitleView - attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual - toItem:self.navigationItem.titleView - attribute:NSLayoutAttributeBottom - multiplier:1.0f - constant:0.0f]; - NSLayoutConstraint *leadingConstraint = [NSLayoutConstraint constraintWithItem:memberTitleView - attribute:NSLayoutAttributeLeading - relatedBy:NSLayoutRelationEqual - toItem:self.navigationItem.titleView - attribute:NSLayoutAttributeLeading - multiplier:1.0f - constant:0.0f]; - NSLayoutConstraint *trailingConstraint = [NSLayoutConstraint constraintWithItem:memberTitleView - attribute:NSLayoutAttributeTrailing - relatedBy:NSLayoutRelationEqual - toItem:self.navigationItem.titleView - attribute:NSLayoutAttributeTrailing - multiplier:1.0f - constant:0.0f]; - [NSLayoutConstraint activateConstraints:@[topConstraint, bottomConstraint, leadingConstraint, trailingConstraint]]; // Register collection view cell class [self.tableView registerClass:TableViewCellWithButton.class forCellReuseIdentifier:[TableViewCellWithButton defaultReuseIdentifier]]; @@ -323,7 +336,8 @@ } } - if (memberTitleView) + // Check whether the title view has been created and rendered. + if (memberTitleView && memberTitleView.superview) { // Adjust the header height by taking into account the actual position of the member avatar in title view // This position depends automatically on the screen orientation. @@ -1047,4 +1061,11 @@ } } +#pragma mark - RoomMemberTitleViewDelegate + +- (void)roomMemberTitleViewDidLayoutSubview:(RoomMemberTitleView*)titleView +{ + [self viewDidLayoutSubviews]; +} + @end diff --git a/Riot/ViewController/RoomMessagesSearchViewController.m b/Riot/ViewController/RoomMessagesSearchViewController.m index 0d1b8f548..2d6afda21 100644 --- a/Riot/ViewController/RoomMessagesSearchViewController.m +++ b/Riot/ViewController/RoomMessagesSearchViewController.m @@ -119,7 +119,7 @@ // Observe kAppDelegateDidTapStatusBarNotificationObserver. kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - [self.searchTableView setContentOffset:CGPointMake(-self.searchTableView.contentInset.left, -self.searchTableView.contentInset.top) animated:YES]; + [self.searchTableView setContentOffset:CGPointMake(-self.searchTableView.mxk_adjustedContentInset.left, -self.searchTableView.mxk_adjustedContentInset.top) animated:YES]; }]; } diff --git a/Riot/ViewController/RoomSettingsViewController.m b/Riot/ViewController/RoomSettingsViewController.m index 68d3ff632..a82fd9151 100644 --- a/Riot/ViewController/RoomSettingsViewController.m +++ b/Riot/ViewController/RoomSettingsViewController.m @@ -46,9 +46,10 @@ #define ROOM_SETTINGS_MAIN_SECTION_ROW_NAME 1 #define ROOM_SETTINGS_MAIN_SECTION_ROW_TOPIC 2 #define ROOM_SETTINGS_MAIN_SECTION_ROW_TAG 3 -#define ROOM_SETTINGS_MAIN_SECTION_ROW_MUTE_NOTIFICATIONS 4 -#define ROOM_SETTINGS_MAIN_SECTION_ROW_LEAVE 5 -#define ROOM_SETTINGS_MAIN_SECTION_ROW_COUNT 6 +#define ROOM_SETTINGS_MAIN_SECTION_ROW_DIRECT_CHAT 4 +#define ROOM_SETTINGS_MAIN_SECTION_ROW_MUTE_NOTIFICATIONS 5 +#define ROOM_SETTINGS_MAIN_SECTION_ROW_LEAVE 6 +#define ROOM_SETTINGS_MAIN_SECTION_ROW_COUNT 7 #define ROOM_SETTINGS_ROOM_ACCESS_SECTION_ROW_INVITED_ONLY 0 #define ROOM_SETTINGS_ROOM_ACCESS_SECTION_ROW_ANYONE_APART_FROM_GUEST 1 @@ -71,6 +72,7 @@ NSString *const kRoomSettingsNameKey = @"kRoomSettingsNameKey"; NSString *const kRoomSettingsTopicKey = @"kRoomSettingsTopicKey"; NSString *const kRoomSettingsTagKey = @"kRoomSettingsTagKey"; NSString *const kRoomSettingsMuteNotifKey = @"kRoomSettingsMuteNotifKey"; +NSString *const kRoomSettingsDirectChatKey = @"kRoomSettingsDirectChatKey"; NSString *const kRoomSettingsJoinRuleKey = @"kRoomSettingsJoinRuleKey"; NSString *const kRoomSettingsGuestAccessKey = @"kRoomSettingsGuestAccessKey"; NSString *const kRoomSettingsDirectoryKey = @"kRoomSettingsDirectoryKey"; @@ -138,11 +140,6 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti // picker MediaPickerViewController* mediaPicker; - // switches - UISwitch *roomNotifSwitch; - UISwitch *roomEncryptionSwitch; - UISwitch *roomEncryptionBlacklistUnverifiedDevicesSwitch; - // Observe kAppDelegateDidTapStatusBarNotification to handle tap on clock status bar. id appDelegateDidTapStatusBarNotificationObserver; @@ -281,7 +278,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti // Observe appDelegateDidTapStatusBarNotificationObserver. appDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - [self.tableView setContentOffset:CGPointMake(-self.tableView.contentInset.left, -self.tableView.contentInset.top) animated:YES]; + [self.tableView setContentOffset:CGPointMake(-self.tableView.mxk_adjustedContentInset.left, -self.tableView.mxk_adjustedContentInset.top) animated:YES]; }]; } @@ -1598,7 +1595,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti if ([updatedItemsDict objectForKey:kRoomSettingsMuteNotifKey]) { - if (roomNotifSwitch.on) + if (((NSNumber*)[updatedItemsDict objectForKey:kRoomSettingsMuteNotifKey]).boolValue) { [mxRoom mentionsOnly:^{ @@ -1629,6 +1626,45 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti return; } + if ([updatedItemsDict objectForKey:kRoomSettingsDirectChatKey]) + { + pendingOperation = [mxRoom setIsDirect:((NSNumber*)[updatedItemsDict objectForKey:kRoomSettingsDirectChatKey]).boolValue withUserId:nil success:^{ + + if (weakSelf) + { + typeof(self) self = weakSelf; + + self->pendingOperation = nil; + [self->updatedItemsDict removeObjectForKey:kRoomSettingsDirectChatKey]; + [self onSave:nil]; + } + + } failure:^(NSError *error) { + + NSLog(@"[RoomSettingsViewController] Altering DMness failed"); + + if (weakSelf) + { + typeof(self) self = weakSelf; + + self->pendingOperation = nil; + + dispatch_async(dispatch_get_main_queue(), ^{ + + NSString* message = error.localizedDescription; + if (!message.length) + { + message = NSLocalizedStringFromTable(@"room_details_fail_to_update_room_direct", @"Vector", nil); + } + [self onSaveFailed:message withKey:kRoomSettingsDirectChatKey]; + + }); + } + + }]; + return; + } + // Room directory visibility MXRoomDirectoryVisibility directoryVisibility = [updatedItemsDict objectForKey:kRoomSettingsDirectoryKey]; if (directoryVisibility) @@ -1944,31 +1980,41 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti { if (row == ROOM_SETTINGS_MAIN_SECTION_ROW_MUTE_NOTIFICATIONS) { - MXKTableViewCellWithLabelAndSwitch *roomNotifCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithLabelAndSwitch defaultReuseIdentifier] forIndexPath:indexPath]; - - roomNotifCell.mxkLabelLeadingConstraint.constant = roomNotifCell.separatorInset.left; - roomNotifCell.mxkSwitchTrailingConstraint.constant = 15; - - [roomNotifCell.mxkSwitch addTarget:self action:@selector(onSwitchUpdate:) forControlEvents:UIControlEventValueChanged]; - roomNotifCell.mxkSwitch.onTintColor = kRiotColorGreen; + MXKTableViewCellWithLabelAndSwitch *roomNotifCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; + + [roomNotifCell.mxkSwitch addTarget:self action:@selector(toggleRoomNotification:) forControlEvents:UIControlEventValueChanged]; roomNotifCell.mxkLabel.text = NSLocalizedStringFromTable(@"room_details_mute_notifs", @"Vector", nil); - roomNotifCell.mxkLabel.textColor = kRiotPrimaryTextColor; - roomNotifSwitch = roomNotifCell.mxkSwitch; if ([updatedItemsDict objectForKey:kRoomSettingsMuteNotifKey]) { - roomNotifSwitch.on = ((NSNumber*)[updatedItemsDict objectForKey:kRoomSettingsMuteNotifKey]).boolValue; + roomNotifCell.mxkSwitch.on = ((NSNumber*)[updatedItemsDict objectForKey:kRoomSettingsMuteNotifKey]).boolValue; } else { - roomNotifSwitch.on = mxRoom.isMute || mxRoom.isMentionsOnly; + roomNotifCell.mxkSwitch.on = mxRoom.isMute || mxRoom.isMentionsOnly; } cell = roomNotifCell; + } + else if (row == ROOM_SETTINGS_MAIN_SECTION_ROW_DIRECT_CHAT) + { + MXKTableViewCellWithLabelAndSwitch *roomDirectChat = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; - // Force layout before reusing a cell (fix switch displayed outside the screen) - [cell layoutIfNeeded]; + [roomDirectChat.mxkSwitch addTarget:self action:@selector(toggleDirectChat:) forControlEvents:UIControlEventValueChanged]; + + roomDirectChat.mxkLabel.text = NSLocalizedStringFromTable(@"room_details_direct_chat", @"Vector", nil); + + if ([updatedItemsDict objectForKey:kRoomSettingsDirectChatKey]) + { + roomDirectChat.mxkSwitch.on = ((NSNumber*)[updatedItemsDict objectForKey:kRoomSettingsDirectChatKey]).boolValue; + } + else + { + roomDirectChat.mxkSwitch.on = mxRoom.isDirect; + } + + cell = roomDirectChat; } else if (row == ROOM_SETTINGS_MAIN_SECTION_ROW_PHOTO) { @@ -2144,51 +2190,29 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti { if (indexPath.row == directoryVisibilityIndex) { - MXKTableViewCellWithLabelAndSwitch *directoryToggleCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithLabelAndSwitch defaultReuseIdentifier] forIndexPath:indexPath]; - - directoryToggleCell.mxkLabelLeadingConstraint.constant = directoryToggleCell.separatorInset.left; - directoryToggleCell.mxkSwitchTrailingConstraint.constant = 15; + MXKTableViewCellWithLabelAndSwitch *directoryToggleCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; directoryToggleCell.mxkLabel.text = NSLocalizedStringFromTable(@"room_details_access_section_directory_toggle", @"Vector", nil); - directoryToggleCell.mxkLabel.textColor = kRiotPrimaryTextColor; - directoryVisibilitySwitch = directoryToggleCell.mxkSwitch; - - // Workaround to avoid mixing between switches - // TODO: this is a design issue with switch within UITableViewCell that must fix everywhere - if (roomEncryptionSwitch == directoryVisibilitySwitch) - { - roomEncryptionSwitch = nil; - } - else if (roomNotifSwitch == directoryVisibilitySwitch) - { - roomNotifSwitch = nil; - } - else if (roomEncryptionBlacklistUnverifiedDevicesSwitch == directoryVisibilitySwitch) - { - roomEncryptionBlacklistUnverifiedDevicesSwitch = nil; - } - - [directoryVisibilitySwitch addTarget:self action:@selector(onSwitchUpdate:) forControlEvents:UIControlEventValueChanged]; - directoryVisibilitySwitch.onTintColor = kRiotColorGreen; + [directoryToggleCell.mxkSwitch addTarget:self action:@selector(toggleDirectoryVisibility:) forControlEvents:UIControlEventValueChanged]; if ([updatedItemsDict objectForKey:kRoomSettingsDirectoryKey]) { - directoryVisibilitySwitch.on = ((NSNumber*)[updatedItemsDict objectForKey:kRoomSettingsDirectoryKey]).boolValue; + directoryToggleCell.mxkSwitch.on = ((NSNumber*)[updatedItemsDict objectForKey:kRoomSettingsDirectoryKey]).boolValue; } else { // Use the last retrieved value if any - directoryVisibilitySwitch.on = actualDirectoryVisibility ? [actualDirectoryVisibility isEqualToString:kMXRoomDirectoryVisibilityPublic] : NO; + directoryToggleCell.mxkSwitch.on = actualDirectoryVisibility ? [actualDirectoryVisibility isEqualToString:kMXRoomDirectoryVisibilityPublic] : NO; } // Check whether the user can change this option - directoryVisibilitySwitch.enabled = (oneSelfPowerLevel >= powerLevels.stateDefault); + directoryToggleCell.mxkSwitch.enabled = (oneSelfPowerLevel >= powerLevels.stateDefault); + + // Store the switch to be able to update it + directoryVisibilitySwitch = directoryToggleCell.mxkSwitch; cell = directoryToggleCell; - - // Force layout before reusing a cell (fix switch displayed outside the screen) - [cell layoutIfNeeded]; } else if (indexPath.row == missingAddressWarningIndex) { @@ -2443,33 +2467,12 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti { if (indexPath.row == 1) { - MXKTableViewCellWithLabelAndSwitch *roomBlacklistUnverifiedDevicesCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithLabelAndSwitch defaultReuseIdentifier] forIndexPath:indexPath]; + MXKTableViewCellWithLabelAndSwitch *roomBlacklistUnverifiedDevicesCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; - roomBlacklistUnverifiedDevicesCell.mxkLabelLeadingConstraint.constant = roomBlacklistUnverifiedDevicesCell.separatorInset.left; - roomBlacklistUnverifiedDevicesCell.mxkSwitchTrailingConstraint.constant = 15; - - [roomBlacklistUnverifiedDevicesCell.mxkSwitch addTarget:self action:@selector(onSwitchUpdate:) forControlEvents:UIControlEventValueChanged]; + [roomBlacklistUnverifiedDevicesCell.mxkSwitch addTarget:self action:@selector(toggleBlacklistUnverifiedDevice:) forControlEvents:UIControlEventValueChanged]; roomBlacklistUnverifiedDevicesCell.mxkSwitch.onTintColor = kRiotColorGreen; roomBlacklistUnverifiedDevicesCell.mxkLabel.text = NSLocalizedStringFromTable(@"room_details_advanced_e2e_encryption_blacklist_unverified_devices", @"Vector", nil); - roomBlacklistUnverifiedDevicesCell.mxkLabel.textColor = kRiotPrimaryTextColor; - - roomEncryptionBlacklistUnverifiedDevicesSwitch = roomBlacklistUnverifiedDevicesCell.mxkSwitch; - - // Workaround to avoid mixing between switches - // TODO: this is a design issue with switch within UITableViewCell that must fix everywhere - if (directoryVisibilitySwitch == roomEncryptionBlacklistUnverifiedDevicesSwitch) - { - directoryVisibilitySwitch = nil; - } - else if (roomNotifSwitch == roomEncryptionBlacklistUnverifiedDevicesSwitch) - { - roomNotifSwitch = nil; - } - else if (roomEncryptionSwitch == roomEncryptionBlacklistUnverifiedDevicesSwitch) - { - roomEncryptionSwitch = nil; - } // For the switch value, use by order: // - the MXCrypto.globalBlacklistUnverifiedDevices if its value is YES @@ -2480,11 +2483,11 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti if (mxRoom.mxSession.crypto.globalBlacklistUnverifiedDevices) { blacklistUnverifiedDevices = YES; - roomEncryptionBlacklistUnverifiedDevicesSwitch.enabled = NO; + roomBlacklistUnverifiedDevicesCell.mxkSwitch.enabled = NO; } else { - roomEncryptionBlacklistUnverifiedDevicesSwitch.enabled = YES; + roomBlacklistUnverifiedDevicesCell.mxkSwitch.enabled = YES; if ([updatedItemsDict objectForKey:kRoomSettingsEncryptionBlacklistUnverifiedDevicesKey]) { @@ -2496,7 +2499,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } } - roomEncryptionBlacklistUnverifiedDevicesSwitch.on = blacklistUnverifiedDevices; + roomBlacklistUnverifiedDevicesCell.mxkSwitch.on = blacklistUnverifiedDevices; cell = roomBlacklistUnverifiedDevicesCell; @@ -2527,40 +2530,15 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti if (oneSelfPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomEncryption]) { - MXKTableViewCellWithLabelAndSwitch *roomEncryptionCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithLabelAndSwitch defaultReuseIdentifier] forIndexPath:indexPath]; + MXKTableViewCellWithLabelAndSwitch *roomEncryptionCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; - roomEncryptionCell.mxkLabelLeadingConstraint.constant = roomEncryptionCell.separatorInset.left; - roomEncryptionCell.mxkSwitchTrailingConstraint.constant = 15; - - [roomEncryptionCell.mxkSwitch addTarget:self action:@selector(onSwitchUpdate:) forControlEvents:UIControlEventValueChanged]; - roomEncryptionCell.mxkSwitch.onTintColor = kRiotColorGreen; + [roomEncryptionCell.mxkSwitch addTarget:self action:@selector(toggleEncryption:) forControlEvents:UIControlEventValueChanged]; roomEncryptionCell.mxkLabel.text = NSLocalizedStringFromTable(@"room_details_advanced_enable_e2e_encryption", @"Vector", nil); - roomEncryptionCell.mxkLabel.textColor = kRiotPrimaryTextColor; - roomEncryptionSwitch = roomEncryptionCell.mxkSwitch; - - // Workaround to avoid mixing between switches - // TODO: this is a design issue with switch within UITableViewCell that must fix everywhere - if (directoryVisibilitySwitch == roomEncryptionSwitch) - { - directoryVisibilitySwitch = nil; - } - else if (roomNotifSwitch == roomEncryptionSwitch) - { - roomNotifSwitch = nil; - } - else if (roomEncryptionBlacklistUnverifiedDevicesSwitch == roomEncryptionSwitch) - { - roomEncryptionBlacklistUnverifiedDevicesSwitch = nil; - } - - roomEncryptionSwitch.on = ([updatedItemsDict objectForKey:kRoomSettingsEncryptionKey] != nil); + roomEncryptionCell.mxkSwitch.on = ([updatedItemsDict objectForKey:kRoomSettingsEncryptionKey] != nil); cell = roomEncryptionCell; - - // Force layout before reusing a cell (fix switch displayed outside the screen) - [cell layoutIfNeeded]; } else { @@ -2608,6 +2586,30 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti // iOS8 requires this method to enable editing (see editActionsForRowAtIndexPath). } +- (MXKTableViewCellWithLabelAndSwitch*)getLabelAndSwitchCell:(UITableView*)tableview forIndexPath:(NSIndexPath *)indexPath +{ + MXKTableViewCellWithLabelAndSwitch *cell = [tableview dequeueReusableCellWithIdentifier:[MXKTableViewCellWithLabelAndSwitch defaultReuseIdentifier] forIndexPath:indexPath]; + + cell.mxkLabelLeadingConstraint.constant = cell.separatorInset.left; + cell.mxkSwitchTrailingConstraint.constant = 15; + + cell.mxkLabel.textColor = kRiotPrimaryTextColor; + + cell.mxkSwitch.onTintColor = kRiotColorGreen; + [cell.mxkSwitch removeTarget:self action:nil forControlEvents:UIControlEventValueChanged]; + + // Reset the stored `directoryVisibilitySwitch` if the corresponding cell is reused. + if (cell.mxkSwitch == directoryVisibilitySwitch) + { + directoryVisibilitySwitch = nil; + } + + // Force layout before reusing a cell (fix switch displayed outside the screen) + [cell layoutIfNeeded]; + + return cell; +} + #pragma mark - UITableViewDelegate - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; @@ -3136,102 +3138,123 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti [self presentViewController:navigationController animated:YES completion:nil]; } -- (void)onSwitchUpdate:(UISwitch*)theSwitch +- (void)toggleRoomNotification:(UISwitch*)theSwitch { - if (theSwitch == roomNotifSwitch) + if (theSwitch.on == (mxRoom.isMute || mxRoom.isMentionsOnly)) { - if (roomNotifSwitch.on == (mxRoom.isMute || mxRoom.isMentionsOnly)) - { - [updatedItemsDict removeObjectForKey:kRoomSettingsMuteNotifKey]; - } - else - { - [updatedItemsDict setObject:[NSNumber numberWithBool:roomNotifSwitch.on] forKey:kRoomSettingsMuteNotifKey]; - } + [updatedItemsDict removeObjectForKey:kRoomSettingsMuteNotifKey]; } - else if (theSwitch == directoryVisibilitySwitch) + else { - MXRoomDirectoryVisibility visibility = directoryVisibilitySwitch.on ? kMXRoomDirectoryVisibilityPublic : kMXRoomDirectoryVisibilityPrivate; + [updatedItemsDict setObject:[NSNumber numberWithBool:theSwitch.on] forKey:kRoomSettingsMuteNotifKey]; + } + + [self getNavigationItem].rightBarButtonItem.enabled = (updatedItemsDict.count != 0); +} + +- (void)toggleDirectChat:(UISwitch*)theSwitch +{ + if (theSwitch.on == mxRoom.isDirect) + { + [updatedItemsDict removeObjectForKey:kRoomSettingsDirectChatKey]; + } + else + { + [updatedItemsDict setObject:[NSNumber numberWithBool:theSwitch.on] forKey:kRoomSettingsDirectChatKey]; + } + + [self getNavigationItem].rightBarButtonItem.enabled = (updatedItemsDict.count != 0); +} + +- (void)toggleEncryption:(UISwitch*)theSwitch +{ + if (theSwitch.on) + { + // Prompt here user before turning on the data encryption + __weak typeof(self) weakSelf = self; - // Check whether the actual settings has been retrieved - if (actualDirectoryVisibility) + [currentAlert dismissViewControllerAnimated:NO completion:nil]; + + currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"warning", @"Vector", nil) + message:NSLocalizedStringFromTable(@"room_details_advanced_e2e_encryption_prompt_message", @"Vector", nil) + preferredStyle:UIAlertControllerStyleAlert]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + } + + // Reset switch change + theSwitch.on = NO; + + }]]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + + [self->updatedItemsDict setObject:@(YES) forKey:kRoomSettingsEncryptionKey]; + + [self getNavigationItem].rightBarButtonItem.enabled = self->updatedItemsDict.count; + } + + }]]; + + [currentAlert mxk_setAccessibilityIdentifier:@"RoomSettingsVCEnableEncryptionAlert"]; + [self presentViewController:currentAlert animated:YES completion:nil]; + } + else + { + [updatedItemsDict removeObjectForKey:kRoomSettingsEncryptionKey]; + } + + [self getNavigationItem].rightBarButtonItem.enabled = (updatedItemsDict.count != 0); +} + +- (void)toggleBlacklistUnverifiedDevice:(UISwitch*)theSwitch +{ + if ([mxRoom.mxSession.crypto isBlacklistUnverifiedDevicesInRoom:mxRoom.roomId] != theSwitch.on) + { + updatedItemsDict[kRoomSettingsEncryptionBlacklistUnverifiedDevicesKey] = @(theSwitch.on); + } + else + { + [updatedItemsDict removeObjectForKey:kRoomSettingsEncryptionBlacklistUnverifiedDevicesKey]; + } + + [self getNavigationItem].rightBarButtonItem.enabled = (updatedItemsDict.count != 0); +} + + +- (void)toggleDirectoryVisibility:(UISwitch*)theSwitch +{ + MXRoomDirectoryVisibility visibility = theSwitch.on ? kMXRoomDirectoryVisibilityPublic : kMXRoomDirectoryVisibilityPrivate; + + // Check whether the actual settings has been retrieved + if (actualDirectoryVisibility) + { + if ([visibility isEqualToString:actualDirectoryVisibility]) { - if ([visibility isEqualToString:actualDirectoryVisibility]) - { - [updatedItemsDict removeObjectForKey:kRoomSettingsDirectoryKey]; - } - else - { - [updatedItemsDict setObject:visibility forKey:kRoomSettingsDirectoryKey]; - } + [updatedItemsDict removeObjectForKey:kRoomSettingsDirectoryKey]; } else { [updatedItemsDict setObject:visibility forKey:kRoomSettingsDirectoryKey]; } } - else if (theSwitch == roomEncryptionSwitch) + else { - if (roomEncryptionSwitch.on) - { - // Prompt here user before turning on the data encryption - __weak typeof(self) weakSelf = self; - - [currentAlert dismissViewControllerAnimated:NO completion:nil]; - - currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"warning", @"Vector", nil) - message:NSLocalizedStringFromTable(@"room_details_advanced_e2e_encryption_prompt_message", @"Vector", nil) - preferredStyle:UIAlertControllerStyleAlert]; - - [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - } - - // Reset switch change - theSwitch.on = NO; - - }]]; - - [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - - [self->updatedItemsDict setObject:@(YES) forKey:kRoomSettingsEncryptionKey]; - - [self getNavigationItem].rightBarButtonItem.enabled = self->updatedItemsDict.count; - } - - }]]; - - [currentAlert mxk_setAccessibilityIdentifier:@"RoomSettingsVCEnableEncryptionAlert"]; - [self presentViewController:currentAlert animated:YES completion:nil]; - } - else - { - [updatedItemsDict removeObjectForKey:kRoomSettingsEncryptionKey]; - } - } - else if (theSwitch == roomEncryptionBlacklistUnverifiedDevicesSwitch) - { - if ([mxRoom.mxSession.crypto isBlacklistUnverifiedDevicesInRoom:mxRoom.roomId] != roomEncryptionBlacklistUnverifiedDevicesSwitch.on) - { - updatedItemsDict[kRoomSettingsEncryptionBlacklistUnverifiedDevicesKey] = @(roomEncryptionBlacklistUnverifiedDevicesSwitch.on); - } - else - { - [updatedItemsDict removeObjectForKey:kRoomSettingsEncryptionBlacklistUnverifiedDevicesKey]; - } + [updatedItemsDict setObject:visibility forKey:kRoomSettingsDirectoryKey]; } [self getNavigationItem].rightBarButtonItem.enabled = (updatedItemsDict.count != 0); diff --git a/Riot/ViewController/RoomViewController.m b/Riot/ViewController/RoomViewController.m index 65a01acef..dc2c4a079 100644 --- a/Riot/ViewController/RoomViewController.m +++ b/Riot/ViewController/RoomViewController.m @@ -110,6 +110,7 @@ #import "MXRoom+Riot.h" #import "IntegrationManagerViewController.h" +#import "WidgetPickerViewController.h" @interface RoomViewController () { @@ -373,6 +374,14 @@ missedDiscussionsBarButtonCustomView.backgroundColor = [UIColor clearColor]; missedDiscussionsBarButtonCustomView.clipsToBounds = NO; + NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:missedDiscussionsBarButtonCustomView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:21]; + missedDiscussionsBadgeLabelBgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 21, 21)]; [missedDiscussionsBadgeLabelBgView.layer setCornerRadius:10]; @@ -398,7 +407,7 @@ multiplier:1.0 constant:0]; - [NSLayoutConstraint activateConstraints:@[centerXConstraint, centerYConstraint]]; + [NSLayoutConstraint activateConstraints:@[heightConstraint, centerXConstraint, centerYConstraint]]; // Set up the room title view according to the data source (if any) [self refreshRoomTitle]; @@ -490,7 +499,7 @@ // Observe kAppDelegateDidTapStatusBarNotification. kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - [self.bubblesTableView setContentOffset:CGPointMake(-self.bubblesTableView.contentInset.left, -self.bubblesTableView.contentInset.top) animated:YES]; + [self.bubblesTableView setContentOffset:CGPointMake(-self.bubblesTableView.mxk_adjustedContentInset.left, -self.bubblesTableView.mxk_adjustedContentInset.top) animated:YES]; }]; } @@ -682,7 +691,7 @@ CGRect frame = expandedHeader.bottomBorderView.frame; self.expandedHeaderContainerHeightConstraint.constant = frame.origin.y + frame.size.height; - self.bubblesTableViewTopConstraint.constant = self.expandedHeaderContainerHeightConstraint.constant - self.bubblesTableView.contentInset.top; + self.bubblesTableViewTopConstraint.constant = self.expandedHeaderContainerHeightConstraint.constant - self.bubblesTableView.mxk_adjustedContentInset.top; self.jumpToLastUnreadBannerContainerTopConstraint.constant = self.expandedHeaderContainerHeightConstraint.constant; } // Check whether the preview header is visible @@ -709,12 +718,12 @@ CGRect frame = previewHeader.bottomBorderView.frame; self.previewHeaderContainerHeightConstraint.constant = frame.origin.y + frame.size.height; - self.bubblesTableViewTopConstraint.constant = self.previewHeaderContainerHeightConstraint.constant - self.bubblesTableView.contentInset.top; + self.bubblesTableViewTopConstraint.constant = self.previewHeaderContainerHeightConstraint.constant - self.bubblesTableView.mxk_adjustedContentInset.top; self.jumpToLastUnreadBannerContainerTopConstraint.constant = self.previewHeaderContainerHeightConstraint.constant; } else { - self.jumpToLastUnreadBannerContainerTopConstraint.constant = self.bubblesTableView.contentInset.top; + self.jumpToLastUnreadBannerContainerTopConstraint.constant = self.bubblesTableView.mxk_adjustedContentInset.top; } [self refreshMissedDiscussionsCount:YES]; @@ -1188,13 +1197,31 @@ barButtonItem.enabled = YES; } - BOOL matrixAppsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"matrixApps"]; - if (!matrixAppsEnabled && self.navigationItem.rightBarButtonItems.count == 2) + if (self.navigationItem.rightBarButtonItems.count == 2) { - // If the setting is disabled, do not show the icon - self.navigationItem.rightBarButtonItems = @[self.navigationItem.rightBarButtonItem]; + BOOL matrixAppsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"matrixApps"]; + if (!matrixAppsEnabled) + { + // If the setting is disabled, do not show the icon + self.navigationItem.rightBarButtonItems = @[self.navigationItem.rightBarButtonItem]; + } + else if (self.widgetsCount) + { + // Show there are widgets by changing the "apps" icon color + // TODO: Design must be reviewed + UIImage *icon = self.navigationItem.rightBarButtonItems[1].image; + icon = [MXKTools paintImage:icon withColor:kRiotColorPinkRed]; + icon = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; + + self.navigationItem.rightBarButtonItems[1].image = icon; + } + else + { + // Reset original icon + self.navigationItem.rightBarButtonItems[1].image = [UIImage imageNamed:@"apps-icon"]; + } } - + // Do not change title view class here if the expanded header is visible. if (self.expandedHeaderContainer.hidden) { @@ -1365,8 +1392,8 @@ [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn animations:^{ - self.bubblesTableViewTopConstraint.constant = (isVisible ? self.expandedHeaderContainerHeightConstraint.constant - self.bubblesTableView.contentInset.top : 0); - self.jumpToLastUnreadBannerContainerTopConstraint.constant = (isVisible ? self.expandedHeaderContainerHeightConstraint.constant : self.bubblesTableView.contentInset.top); + self.bubblesTableViewTopConstraint.constant = (isVisible ? self.expandedHeaderContainerHeightConstraint.constant - self.bubblesTableView.mxk_adjustedContentInset.top : 0); + self.jumpToLastUnreadBannerContainerTopConstraint.constant = (isVisible ? self.expandedHeaderContainerHeightConstraint.constant : self.bubblesTableView.mxk_adjustedContentInset.top); if (roomAvatarView) { @@ -1481,7 +1508,7 @@ animations:^{ self.bubblesTableViewTopConstraint.constant = 0; - self.jumpToLastUnreadBannerContainerTopConstraint.constant = self.bubblesTableView.contentInset.top; + self.jumpToLastUnreadBannerContainerTopConstraint.constant = self.bubblesTableView.mxk_adjustedContentInset.top; // Force to render the view [self forceLayoutRefresh]; @@ -1572,7 +1599,7 @@ [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn animations:^{ - self.bubblesTableViewTopConstraint.constant = self.previewHeaderContainerHeightConstraint.constant - self.bubblesTableView.contentInset.top; + self.bubblesTableViewTopConstraint.constant = self.previewHeaderContainerHeightConstraint.constant - self.bubblesTableView.mxk_adjustedContentInset.top; self.jumpToLastUnreadBannerContainerTopConstraint.constant = self.previewHeaderContainerHeightConstraint.constant; if (roomAvatarView) @@ -2880,12 +2907,23 @@ // Matrix Apps button else if (self.navigationItem.rightBarButtonItems.count == 2 && sender == self.navigationItem.rightBarButtonItems[1]) { - IntegrationManagerViewController *modularVC = [[IntegrationManagerViewController alloc] initForMXSession:self.roomDataSource.mxSession - inRoom:self.roomDataSource.roomId - screen:kIntegrationManagerMainScreen - widgetId:nil]; + if (self.widgetsCount) + { + WidgetPickerViewController *widgetPicker = [[WidgetPickerViewController alloc] initForMXSession:self.roomDataSource.mxSession + inRoom:self.roomDataSource.roomId]; - [self presentViewController:modularVC animated:NO completion:nil]; + [widgetPicker showInViewController:self]; + } + else + { + // No widgets -> Directly show the integration manager + IntegrationManagerViewController *modularVC = [[IntegrationManagerViewController alloc] initForMXSession:self.roomDataSource.mxSession + inRoom:self.roomDataSource.roomId + screen:kIntegrationManagerMainScreen + widgetId:nil]; + + [self presentViewController:modularVC animated:NO completion:nil]; + } } else if (sender == self.jumpToLastUnreadButton) { @@ -2978,7 +3016,7 @@ // Switch back to the live mode when the user scrolls to the bottom of the non live timeline. if (!self.roomDataSource.isLive && ![self isRoomPreview]) { - CGFloat contentBottomPosY = self.bubblesTableView.contentOffset.y + self.bubblesTableView.frame.size.height - self.bubblesTableView.contentInset.bottom; + CGFloat contentBottomPosY = self.bubblesTableView.contentOffset.y + self.bubblesTableView.frame.size.height - self.bubblesTableView.mxk_adjustedContentInset.bottom; if (contentBottomPosY >= self.bubblesTableView.contentSize.height && ![self.roomDataSource.timeline canPaginate:MXTimelineDirectionForwards]) { [self goBackToLive]; @@ -3430,6 +3468,7 @@ // Update the bar [self refreshActivitiesViewDisplay]; [self refreshRoomInputToolbar]; + [self refreshRoomTitle]; } }]; } @@ -3450,6 +3489,12 @@ [[AppDelegate theDelegate] showErrorAsAlert:error]; } +- (NSUInteger)widgetsCount +{ + return [[WidgetManager sharedManager] widgetsNotOfTypes:@[kWidgetTypeJitsi] + inRoom:self.roomDataSource.room].count; +} + #pragma mark - Unreachable Network Handling - (void)refreshActivitiesViewDisplay @@ -3685,15 +3730,6 @@ if (missedCount) { - // Consider the main navigation controller if the current view controller is embedded inside a split view controller. - UINavigationController *mainNavigationController = self.navigationController; - if (self.splitViewController.isCollapsed && self.splitViewController.viewControllers.count) - { - mainNavigationController = self.splitViewController.viewControllers.firstObject; - } - UINavigationItem *backItem = mainNavigationController.navigationBar.backItem; - UIBarButtonItem *backButton = backItem.backBarButtonItem; - // Refresh missed discussions count label if (missedCount > 99) { @@ -3709,25 +3745,33 @@ // Update the label background view frame CGRect frame = missedDiscussionsBadgeLabelBgView.frame; frame.size.width = round(missedDiscussionsBadgeLabel.frame.size.width + 18); - if (backButton && !backButton.title.length) + + if ([GBDeviceInfo deviceInfo].osVersion.major < 11) { - // Shift the badge on the left to be close the back icon - frame.origin.x = ([GBDeviceInfo deviceInfo].displayInfo.display > GBDeviceDisplay4Inch ? -35 : -25); - } - else - { - frame.origin.x = 0; + // Consider the main navigation controller if the current view controller is embedded inside a split view controller. + UINavigationController *mainNavigationController = self.navigationController; + if (self.splitViewController.isCollapsed && self.splitViewController.viewControllers.count) + { + mainNavigationController = self.splitViewController.viewControllers.firstObject; + } + UINavigationItem *backItem = mainNavigationController.navigationBar.backItem; + UIBarButtonItem *backButton = backItem.backBarButtonItem; + + if (backButton && !backButton.title.length) + { + // Shift the badge on the left to be close the back icon + frame.origin.x = ([GBDeviceInfo deviceInfo].displayInfo.display > GBDeviceDisplay4Inch ? -35 : -25); + } + else + { + frame.origin.x = 0; + } } + // Caution: set label background view frame only in case of changes to prevent from looping on 'viewDidLayoutSubviews'. if (!CGRectEqualToRect(missedDiscussionsBadgeLabelBgView.frame, frame)) { missedDiscussionsBadgeLabelBgView.frame = frame; - - // Adjust the custom view width of the associated bar button - CGRect bgFrame = missedDiscussionsBarButtonCustomView.frame; - CGFloat width = frame.size.width + frame.origin.x; - bgFrame.size.width = (width > 0 ? width : 0); - missedDiscussionsBarButtonCustomView.frame = bgFrame; } // Set the right background color @@ -4060,12 +4104,12 @@ if (readMarkerTableViewCell && isAppeared && !self.isBubbleTableViewDisplayInTransition) { // Check whether the read marker is visible - CGFloat contentTopPosY = self.bubblesTableView.contentOffset.y + self.bubblesTableView.contentInset.top; + CGFloat contentTopPosY = self.bubblesTableView.contentOffset.y + self.bubblesTableView.mxk_adjustedContentInset.top; CGFloat readMarkerViewPosY = readMarkerTableViewCell.frame.origin.y + readMarkerTableViewCell.readMarkerView.frame.origin.y; if (contentTopPosY <= readMarkerViewPosY) { // Compute the max vertical position visible according to contentOffset - CGFloat contentBottomPosY = self.bubblesTableView.contentOffset.y + self.bubblesTableView.frame.size.height - self.bubblesTableView.contentInset.bottom; + CGFloat contentBottomPosY = self.bubblesTableView.contentOffset.y + self.bubblesTableView.frame.size.height - self.bubblesTableView.mxk_adjustedContentInset.bottom; if (readMarkerViewPosY <= contentBottomPosY) { // Launch animation @@ -4147,7 +4191,7 @@ // The read marker display is still enabled (see roomDataSource.showReadMarker flag), // this means the read marker was not been visible yet. // We show the banner if the marker is located in the top hidden part of the cell. - CGFloat contentTopPosY = self.bubblesTableView.contentOffset.y + self.bubblesTableView.contentInset.top; + CGFloat contentTopPosY = self.bubblesTableView.contentOffset.y + self.bubblesTableView.mxk_adjustedContentInset.top; CGFloat readMarkerViewPosY = roomBubbleTableViewCell.frame.origin.y + roomBubbleTableViewCell.readMarkerView.frame.origin.y; self.jumpToLastUnreadBannerContainer.hidden = (contentTopPosY < readMarkerViewPosY); } diff --git a/Riot/ViewController/RoomsViewController.m b/Riot/ViewController/RoomsViewController.m index 2ba04c107..64b6c121f 100644 --- a/Riot/ViewController/RoomsViewController.m +++ b/Riot/ViewController/RoomsViewController.m @@ -156,34 +156,47 @@ NSArray *roomDirectoryServers = [[NSUserDefaults standardUserDefaults] objectForKey:@"roomDirectoryServers"]; directoryServersDataSource.roomDirectoryServers = roomDirectoryServers; + __weak typeof(self) weakSelf = self; + [directoryServerPickerViewController displayWithDataSource:directoryServersDataSource onComplete:^(id cellData) { - if (cellData) + if (weakSelf && cellData) { + typeof(self) self = weakSelf; + // Use the selected directory server if (cellData.thirdPartyProtocolInstance) { - recentsDataSource.publicRoomsDirectoryDataSource.thirdpartyProtocolInstance = cellData.thirdPartyProtocolInstance; + self->recentsDataSource.publicRoomsDirectoryDataSource.thirdpartyProtocolInstance = cellData.thirdPartyProtocolInstance; } else if (cellData.homeserver) { - recentsDataSource.publicRoomsDirectoryDataSource.includeAllNetworks = cellData.includeAllNetworks; - recentsDataSource.publicRoomsDirectoryDataSource.homeserver = cellData.homeserver; + self->recentsDataSource.publicRoomsDirectoryDataSource.includeAllNetworks = cellData.includeAllNetworks; + self->recentsDataSource.publicRoomsDirectoryDataSource.homeserver = cellData.homeserver; } // Refresh data [self addSpinnerFooterView]; - [recentsDataSource.publicRoomsDirectoryDataSource paginate:^(NSUInteger roomsAdded) { + [self->recentsDataSource.publicRoomsDirectoryDataSource paginate:^(NSUInteger roomsAdded) { - // The table view is automatically filled - [self removeSpinnerFooterView]; + if (weakSelf) + { + typeof(self) self = weakSelf; - // Make the directory section appear full-page - [self.recentsTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:recentsDataSource.directorySection] atScrollPosition:UITableViewScrollPositionTop animated:YES]; + // The table view is automatically filled + [self removeSpinnerFooterView]; + + // Make the directory section appear full-page + [self.recentsTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:self->recentsDataSource.directorySection] atScrollPosition:UITableViewScrollPositionTop animated:YES]; + } } failure:^(NSError *error) { - [self removeSpinnerFooterView]; + if (weakSelf) + { + typeof(self) self = weakSelf; + [self removeSpinnerFooterView]; + } }]; } }]; diff --git a/Riot/ViewController/SettingsViewController.h b/Riot/ViewController/SettingsViewController.h index 6713cb94d..6c9bff125 100644 --- a/Riot/ViewController/SettingsViewController.h +++ b/Riot/ViewController/SettingsViewController.h @@ -19,9 +19,8 @@ #import "DeviceView.h" #import "MediaPickerViewController.h" -#import "TableViewCellWithCheckBoxes.h" -@interface SettingsViewController : MXKTableViewController +@interface SettingsViewController : MXKTableViewController @end diff --git a/Riot/ViewController/SettingsViewController.m b/Riot/ViewController/SettingsViewController.m index 26c461620..01462e21b 100644 --- a/Riot/ViewController/SettingsViewController.m +++ b/Riot/ViewController/SettingsViewController.m @@ -40,6 +40,8 @@ #import "OLMKit/OLMKit.h" +#import "GBDeviceInfo_iOS.h" + NSString* const kSettingsViewControllerPhoneBookCountryCellId = @"kSettingsViewControllerPhoneBookCountryCellId"; @@ -205,9 +207,6 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); BOOL keepNewEmailEditing; BOOL keepNewPhoneNumberEditing; - // The user interface theme cell - TableViewCellWithCheckBoxes *uiThemeCell; - // The current pushed view controller UIViewController *pushedViewController; } @@ -250,7 +249,6 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); [self.tableView registerClass:MXKTableViewCellWithLabelAndSwitch.class forCellReuseIdentifier:[MXKTableViewCellWithLabelAndSwitch defaultReuseIdentifier]]; [self.tableView registerClass:MXKTableViewCellWithLabelAndMXKImageView.class forCellReuseIdentifier:[MXKTableViewCellWithLabelAndMXKImageView defaultReuseIdentifier]]; [self.tableView registerClass:TableViewCellWithPhoneNumberTextField.class forCellReuseIdentifier:[TableViewCellWithPhoneNumberTextField defaultReuseIdentifier]]; - [self.tableView registerClass:TableViewCellWithCheckBoxes.class forCellReuseIdentifier:[TableViewCellWithCheckBoxes defaultReuseIdentifier]]; // Enable self sizing cells self.tableView.rowHeight = UITableViewAutomaticDimension; @@ -417,7 +415,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); // Observe kAppDelegateDidTapStatusBarNotificationObserver. kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - [self.tableView setContentOffset:CGPointMake(-self.tableView.contentInset.left, -self.tableView.contentInset.top) animated:YES]; + [self.tableView setContentOffset:CGPointMake(-self.tableView.mxk_adjustedContentInset.left, -self.tableView.mxk_adjustedContentInset.top) animated:YES]; }]; @@ -1695,36 +1693,39 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); } else if (row == USER_INTERFACE_THEME_INDEX) { - uiThemeCell = [tableView dequeueReusableCellWithIdentifier:[TableViewCellWithCheckBoxes defaultReuseIdentifier] forIndexPath:indexPath]; - - uiThemeCell.mainContainerLeadingConstraint.constant = uiThemeCell.separatorInset.left; - - uiThemeCell.checkBoxesNumber = 2; - - uiThemeCell.allowsMultipleSelection = NO; - uiThemeCell.delegate = self; - - NSArray *labels = uiThemeCell.labels; - UILabel *label; - label = labels[0]; - label.textColor = kRiotPrimaryTextColor; - label.text = NSLocalizedStringFromTable(@"settings_ui_light_theme", @"Vector", nil); - label = labels[1]; - label.textColor = kRiotPrimaryTextColor; - label.text = NSLocalizedStringFromTable(@"settings_ui_dark_theme", @"Vector", nil); - - NSString *selectedTheme = [[NSUserDefaults standardUserDefaults] stringForKey:@"userInterfaceTheme"]; - if (selectedTheme && [selectedTheme isEqualToString:@"dark"]) + cell = [tableView dequeueReusableCellWithIdentifier:kSettingsViewControllerPhoneBookCountryCellId]; + if (!cell) { - [uiThemeCell setCheckBoxValue:YES atIndex:1]; + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:kSettingsViewControllerPhoneBookCountryCellId]; } - else + + NSString *theme = [[NSUserDefaults standardUserDefaults] stringForKey:@"userInterfaceTheme"]; + if (!theme) { - // Consider the light theme by default. - [uiThemeCell setCheckBoxValue:YES atIndex:0]; + if (@available(iOS 11.0, *)) + { + // "auto" is used the default value from iOS 11 + theme = @"auto"; + } + else + { + // Use "light" for older version + theme = @"light"; + } } - - cell = uiThemeCell; + + theme = [NSString stringWithFormat:@"settings_ui_theme_%@", theme]; + NSString *i18nTheme = NSLocalizedStringFromTable(theme, + @"Vector", + nil); + + cell.textLabel.textColor = kRiotPrimaryTextColor; + + cell.textLabel.text = NSLocalizedStringFromTable(@"settings_ui_theme", @"Vector", nil); + cell.detailTextLabel.text = i18nTheme; + + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + cell.selectionStyle = UITableViewCellSelectionStyleDefault; } } else if (section == SETTINGS_SECTION_IGNORED_USERS_INDEX) @@ -2270,6 +2271,10 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); languagePickerViewController.delegate = self; [self pushViewController:languagePickerViewController]; } + else if (row == USER_INTERFACE_THEME_INDEX) + { + [self showThemePicker]; + } } else if (section == SETTINGS_SECTION_IGNORED_USERS_INDEX) { @@ -3430,6 +3435,90 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); } } +- (void)showThemePicker +{ + __weak typeof(self) weakSelf = self; + + __block UIAlertAction *autoAction, *lightAction, *darkAction; + NSString *themePickerMessage; + + void (^actionBlock)(UIAlertAction *action) = ^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + + NSString *newTheme; + if (action == autoAction) + { + newTheme = @"auto"; + } + else if (action == lightAction) + { + newTheme = @"light"; + } + else if (action == darkAction) + { + newTheme = @"dark"; + } + + NSString *theme = [[NSUserDefaults standardUserDefaults] stringForKey:@"userInterfaceTheme"]; + if (newTheme && ![newTheme isEqualToString:theme]) + { + // Clear fake Riot Avatars based on the previous theme. + [AvatarGenerator clear]; + + // The user wants to select this theme + [[NSUserDefaults standardUserDefaults] setObject:newTheme forKey:@"userInterfaceTheme"]; + [[NSUserDefaults standardUserDefaults] synchronize]; + + [self.tableView reloadData]; + } + } + }; + + if (@available(iOS 11.0, *)) + { + // Show "auto" only from iOS 11 + autoAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"settings_ui_theme_auto", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:actionBlock]; + + // Explain what is "auto" + themePickerMessage = NSLocalizedStringFromTable(@"settings_ui_theme_picker_message", @"Vector", nil); + } + + lightAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"settings_ui_theme_light", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:actionBlock]; + + darkAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"settings_ui_theme_dark", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:actionBlock]; + + UIAlertController *themePicker = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"settings_ui_theme_picker_title", @"Vector", nil) + message:themePickerMessage + preferredStyle:UIAlertControllerStyleActionSheet]; + + if (autoAction) + { + [themePicker addAction:autoAction]; + } + [themePicker addAction:lightAction]; + [themePicker addAction:darkAction]; + + // Cancel button + [themePicker addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] + style:UIAlertActionStyleDefault + handler:nil]]; + + UIView *fromCell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:USER_INTERFACE_THEME_INDEX inSection:SETTINGS_SECTION_USER_INTERFACE_INDEX]]; + [themePicker popoverPresentationController].sourceView = fromCell; + [themePicker popoverPresentationController].sourceRect = fromCell.bounds; + + [self presentViewController:themePicker animated:YES completion:nil]; +} + #pragma mark - MediaPickerViewController Delegate - (void)dismissMediaPicker @@ -3769,25 +3858,4 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); } } -#pragma mark - TableViewCellWithCheckBoxesDelegate - -- (void)tableViewCellWithCheckBoxes:(TableViewCellWithCheckBoxes *)tableViewCellWithCheckBoxes didTapOnCheckBoxAtIndex:(NSUInteger)index -{ - if (tableViewCellWithCheckBoxes == uiThemeCell) - { - NSString *theme = (index == 0) ? @"light" : @"dark"; - BOOL isCurrentlySelected = [uiThemeCell checkBoxValueAtIndex:index]; - - if (!isCurrentlySelected) - { - // Clear fake Riot Avatars based on the previous theme. - [AvatarGenerator clear]; - - // The user wants to select this theme - [[NSUserDefaults standardUserDefaults] setObject:theme forKey:@"userInterfaceTheme"]; - [[NSUserDefaults standardUserDefaults] synchronize]; - } - } -} - @end diff --git a/Riot/ViewController/Widgets/IntegrationManagerViewController.m b/Riot/ViewController/Widgets/IntegrationManagerViewController.m index f52e9fa99..742c522e8 100644 --- a/Riot/ViewController/Widgets/IntegrationManagerViewController.m +++ b/Riot/ViewController/Widgets/IntegrationManagerViewController.m @@ -17,6 +17,7 @@ #import "IntegrationManagerViewController.h" #import "WidgetManager.h" +#import "AppDelegate.h" #import @@ -69,6 +70,9 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@', webView.scalesPageToFit = NO; webView.scrollView.bounces = NO; + // Disable opacity so that the webview background uses the current interface theme + webView.opaque = NO; + webView.delegate = self; } @@ -90,7 +94,6 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@', if (self) { self->operation = nil; - [self stopActivityIndicator]; scalarToken = theScalarToken; @@ -156,6 +159,43 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@', [webView stringByEvaluatingJavaScriptFromString:@"console.debug = console.log; console.info = console.log; console.warn = console.log; console.error = console.log;"]; } +- (void)showErrorAsAlert:(NSError*)error +{ + NSString *title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey]; + NSString *msg = [error.userInfo valueForKey:NSLocalizedDescriptionKey]; + if (!title) + { + if (msg) + { + title = msg; + msg = nil; + } + else + { + title = [NSBundle mxk_localizedStringForKey:@"error"]; + } + } + + __weak __typeof__(self) weakSelf = self; + + UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert]; + [alert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + typeof(self) self = weakSelf; + + if (self) + { + // Leave this Intergrations Manager VC + [self withdrawViewControllerAnimated:YES completion:nil]; + } + + }]]; + + [self presentViewController:alert animated:YES completion:nil]; +} + #pragma mark - UIWebViewDelegate -(void)webViewDidFinishLoad:(UIWebView *)theWebView @@ -166,6 +206,22 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@', NSString *path = [[NSBundle mainBundle] pathForResource:@"IntegrationManager" ofType:@"js"]; NSString *js = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; [webView stringByEvaluatingJavaScriptFromString:js]; + + [self stopActivityIndicator]; + + // Check connectivity + if ([AppDelegate theDelegate].isOffline) + { + // The web page may be in the cache, so its loading will be successful + // but we cannot go further, it often leads to a blank screen. + // So, display an error so that the user can escape. + NSError *error = [NSError errorWithDomain:NSURLErrorDomain + code:NSURLErrorNotConnectedToInternet + userInfo:@{ + NSLocalizedDescriptionKey : NSLocalizedStringFromTable(@"network_offline_prompt", @"Vector", nil) + }]; + [self showErrorAsAlert:error]; + } } - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType @@ -188,9 +244,34 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@', return NO; } + + if (navigationType == UIWebViewNavigationTypeLinkClicked ) + { + // Open links outside the app + [[UIApplication sharedApplication] openURL:[request URL]]; + return NO; + } + return YES; } +- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error +{ + // Filter out the users's scalar token + NSString *errorDescription = error.description; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"scalar_token=\\w*" + options:NSRegularExpressionCaseInsensitive error:nil]; + errorDescription = [regex stringByReplacingMatchesInString:errorDescription + options:0 + range:NSMakeRange(0, errorDescription.length) + withTemplate:@"scalar_token=..."]; + + NSLog(@"[IntegrationManagerVC] didFailLoadWithError: %@", errorDescription); + + [self stopActivityIndicator]; + [self showErrorAsAlert:error]; +} + #pragma mark - Modular postMessage API - (void)onMessage:(NSDictionary*)JSData @@ -368,8 +449,9 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@', { NSLog(@"[IntegrationManagerVC] Received request to invite %@ into room %@.", userId, roomId); - MXRoom *room = [mxSession roomWithRoomId:roomId]; - if (!room) + MXRoom *room = [self roomCheckWithEvent:eventData]; + + if (room) { MXRoomMember *member = [room.state memberWithUserId:userId]; if (member && member.membership == MXMembershipJoin) @@ -549,7 +631,7 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@', if (room) { MXRoomMember *member = [room.state memberWithUserId:userId]; - [self sendNSObjectResponse:member.originalEvent.JSONDictionary toEvent:eventData]; + [self sendNSObjectResponse:member.originalEvent.content toEvent:eventData]; } } diff --git a/Riot/ViewController/Widgets/WidgetPickerViewController.h b/Riot/ViewController/Widgets/WidgetPickerViewController.h new file mode 100644 index 000000000..8293c1029 --- /dev/null +++ b/Riot/ViewController/Widgets/WidgetPickerViewController.h @@ -0,0 +1,56 @@ +/* + Copyright 2017 Vector Creations 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 + +#import +#import + +/** + `WidgetPickerViewController` displays the list of widgets within a room plus a + way to open the integration manager for this room. + + TODO: The feature is still in dev. WidgetPickerViewController` is not yet a pure + UIViewController. + As there is no specified design, the list is displayed in a simple UIAlertController. + It would be nice if this picker could directly: + - Remove a widget + - Launch the integration manager to edit a widget + - Automatically updates on widgets change + */ +@interface WidgetPickerViewController : NSObject + +/** + The UIAlertController instance which handles the dialog. + */ +@property (nonatomic, readonly) UIAlertController *alertController; + +/** + Create the `WidgetPickerViewController` instance. + + @param mxSession the session to use. + @param roomId the room where to list available widgets. + */ +- (instancetype)initForMXSession:(MXSession*)mxSession inRoom:(NSString*)roomId; + +/** + Show the dialog in a given view controller. + + @param mxkViewController the mxkViewController where to show the dialog. + */ +- (void)showInViewController:(MXKViewController*)mxkViewController; + +@end diff --git a/Riot/ViewController/Widgets/WidgetPickerViewController.m b/Riot/ViewController/Widgets/WidgetPickerViewController.m new file mode 100644 index 000000000..a083fc3a3 --- /dev/null +++ b/Riot/ViewController/Widgets/WidgetPickerViewController.m @@ -0,0 +1,99 @@ +/* + Copyright 2017 Vector Creations 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 "WidgetPickerViewController.h" + +#import "AppDelegate.h" + +#import "WidgetManager.h" +#import "WidgetViewController.h" +#import "IntegrationManagerViewController.h" + +@interface WidgetPickerViewController () +{ + MXSession *mxSession; + NSString *roomId; +} + +@end + +@implementation WidgetPickerViewController + +- (instancetype)initForMXSession:(MXSession*)theMXSession inRoom:(NSString*)theRoomId +{ + self = [super init]; + if (self) + { + mxSession = theMXSession; + roomId = theRoomId; + + _alertController = [UIAlertController alertControllerWithTitle:@"Matrix Apps" + message:nil + preferredStyle:UIAlertControllerStyleAlert]; + } + return self; +} + +- (void)showInViewController:(MXKViewController *)mxkViewController +{ + UIAlertAction *alertAction; + + MXRoom *room = [mxSession roomWithRoomId:roomId]; + + NSArray *widgets = [[WidgetManager sharedManager] widgetsNotOfTypes:@[kWidgetTypeJitsi] + inRoom:room]; + // List widgets + for (Widget *widget in widgets) + { + alertAction = [UIAlertAction actionWithTitle:widget.name + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) + { + // Hide back button title + mxkViewController.navigationItem.backBarButtonItem =[[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil]; + + // Display the widget + WidgetViewController *widgetVC = [[WidgetViewController alloc] initForWidget:widget]; + [mxkViewController.navigationController pushViewController:widgetVC animated:YES]; + }]; + [_alertController addAction:alertAction]; + } + + // Link to the integration manager + alertAction = [UIAlertAction actionWithTitle:@"Manage integrations..." + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) + { + IntegrationManagerViewController *modularVC = [[IntegrationManagerViewController alloc] initForMXSession:self->mxSession + inRoom:self->roomId + screen:kIntegrationManagerMainScreen + widgetId:nil]; + + [mxkViewController presentViewController:modularVC animated:NO completion:nil]; + }]; + [_alertController addAction:alertAction]; + + // Cancel + alertAction = [UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] + style:UIAlertActionStyleCancel + handler:nil]; + [_alertController addAction:alertAction]; + + // And show it + [mxkViewController presentViewController:_alertController animated:YES completion:nil]; +} + +@end diff --git a/Riot/ViewController/Widgets/WidgetViewController.h b/Riot/ViewController/Widgets/WidgetViewController.h new file mode 100644 index 000000000..eb439a3ac --- /dev/null +++ b/Riot/ViewController/Widgets/WidgetViewController.h @@ -0,0 +1,33 @@ +/* + Copyright 2017 Vector Creations 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 "WebViewViewController.h" + +#import "WidgetManager.h" + +/** + `WidgetViewController` displays widget within a webview. + */ +@interface WidgetViewController : WebViewViewController + +/** + Init 'WidgetViewController' instance with a widget. + + @param widget the widget to open. + */ +- (instancetype)initForWidget:(Widget*)widget; + +@end diff --git a/Riot/ViewController/Widgets/WidgetViewController.m b/Riot/ViewController/Widgets/WidgetViewController.m new file mode 100644 index 000000000..355306fac --- /dev/null +++ b/Riot/ViewController/Widgets/WidgetViewController.m @@ -0,0 +1,48 @@ +/* + Copyright 2017 Vector Creations 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 "WidgetViewController.h" + +@interface WidgetViewController () +{ + Widget *widget; +} + +@end + +@implementation WidgetViewController + +- (instancetype)initForWidget:(Widget*)theWidget +{ + self = [super initWithURL:theWidget.url]; + if (self) + { + widget = theWidget; + } + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + webView.scalesPageToFit = NO; + webView.scrollView.bounces = NO; + + self.navigationItem.title = widget.name; +} + +@end diff --git a/Riot/Views/RoomBubbleList/RoomEmptyBubbleCell.m b/Riot/Views/RoomBubbleList/RoomEmptyBubbleCell.m index 71ba494ee..5b7fe73b2 100644 --- a/Riot/Views/RoomBubbleList/RoomEmptyBubbleCell.m +++ b/Riot/Views/RoomBubbleList/RoomEmptyBubbleCell.m @@ -20,6 +20,8 @@ - (void)prepareForReuse { + [super prepareForReuse]; + if (self.heightConstraint != 0) { self.heightConstraint = 0; diff --git a/Riot/Views/RoomBubbleList/RoomMembershipBubbleCell.m b/Riot/Views/RoomBubbleList/RoomMembershipBubbleCell.m index 5e25cf6e1..5073402c3 100644 --- a/Riot/Views/RoomBubbleList/RoomMembershipBubbleCell.m +++ b/Riot/Views/RoomBubbleList/RoomMembershipBubbleCell.m @@ -45,6 +45,8 @@ - (void)prepareForReuse { + [super prepareForReuse]; + if (self.pictureViewTopConstraint.constant != xibPictureViewTopConstraintConstant) { self.pictureViewTopConstraint.constant = xibPictureViewTopConstraintConstant; diff --git a/Riot/Views/RoomBubbleList/RoomMembershipCollapsedBubbleCell.m b/Riot/Views/RoomBubbleList/RoomMembershipCollapsedBubbleCell.m index 193a8d6ca..b24f91fac 100644 --- a/Riot/Views/RoomBubbleList/RoomMembershipCollapsedBubbleCell.m +++ b/Riot/Views/RoomBubbleList/RoomMembershipCollapsedBubbleCell.m @@ -45,6 +45,8 @@ - (void)prepareForReuse { + [super prepareForReuse]; + // Reset avatars for (UIView *avatarView in self.avatarsView.subviews) { diff --git a/Riot/Views/RoomInputToolbar/KeyboardGrowingTextView.m b/Riot/Views/RoomInputToolbar/KeyboardGrowingTextView.m new file mode 100644 index 000000000..702a4b35f --- /dev/null +++ b/Riot/Views/RoomInputToolbar/KeyboardGrowingTextView.m @@ -0,0 +1,41 @@ +/* + Copyright 2015 OpenMarket Ltd + Copyright 2017 Vector Creations 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 +#import +#import "RoomInputToolbarView.h" + +@interface KeyboardGrowingTextView: HPGrowingTextView +- (NSArray *)keyCommands; +@end + +@implementation KeyboardGrowingTextView + +- (NSArray *)keyCommands { + return @[ + [UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:0 action:@selector(keyCommandSelector:)] + ]; +} + +- (void)keyCommandSelector:(UIKeyCommand *)sender { + if ([sender.input isEqualToString:@"\r"] && [self.delegate isKindOfClass: RoomInputToolbarView.class]){ + RoomInputToolbarView *ritv = (RoomInputToolbarView *)self.delegate; + [ritv onTouchUpInside:ritv.rightInputToolbarButton]; // touch the Send button. + } +} + +@end diff --git a/Riot/Views/RoomInputToolbar/RoomInputToolbarView.xib b/Riot/Views/RoomInputToolbar/RoomInputToolbarView.xib index 09895caa8..dd571895a 100644 --- a/Riot/Views/RoomInputToolbar/RoomInputToolbarView.xib +++ b/Riot/Views/RoomInputToolbar/RoomInputToolbarView.xib @@ -46,7 +46,7 @@ - + diff --git a/Riot/Views/RoomMember/RoomMemberTitleView.h b/Riot/Views/RoomMember/RoomMemberTitleView.h index 05e12d66c..302160d31 100644 --- a/Riot/Views/RoomMember/RoomMemberTitleView.h +++ b/Riot/Views/RoomMember/RoomMemberTitleView.h @@ -16,6 +16,20 @@ #import +// We add here a protocol to handle title view layout update. +@class RoomMemberTitleView; +@protocol RoomMemberTitleViewDelegate + +@optional +/** + Tells the delegate that the layout has been updated. + + @param titleView the room member title view. + */ +- (void)roomMemberTitleViewDidLayoutSubview:(RoomMemberTitleView*)titleView; + +@end + @interface RoomMemberTitleView : MXKView /** @@ -38,4 +52,9 @@ @property (weak, nonatomic) IBOutlet UIImageView *memberBadge; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *memberAvatarCenterXConstraint; +/** + The delegate. + */ +@property (nonatomic) id delegate; + @end diff --git a/Riot/Views/RoomMember/RoomMemberTitleView.m b/Riot/Views/RoomMember/RoomMemberTitleView.m index 8cbdc856b..7eabc79fa 100644 --- a/Riot/Views/RoomMember/RoomMemberTitleView.m +++ b/Riot/Views/RoomMember/RoomMemberTitleView.m @@ -40,29 +40,60 @@ if (self.superview) { - // Center horizontally the avatar into the navigation bar - CGRect frame = self.superview.frame; - UINavigationBar *navigationBar; - UIView *superView = self; - while (superView.superview) + if (@available(iOS 11.0, *)) { - if ([superView.superview isKindOfClass:[UINavigationBar class]]) + // Force the title view layout by adding 2 new constraints on the UINavigationBarContentView instance. + NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeTop + multiplier:1.0f + constant:0.0f]; + NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeCenterX + multiplier:1.0f + constant:0.0f]; + + [NSLayoutConstraint activateConstraints:@[topConstraint, centerXConstraint]]; + + // Do not crop the avatar + self.superview.clipsToBounds = NO; + } + else + { + // Center horizontally the avatar into the navigation bar + CGRect frame = self.superview.frame; + UINavigationBar *navigationBar; + UIView *superView = self; + while (superView.superview) { - navigationBar = (UINavigationBar*)superView.superview; - break; + if ([superView.superview isKindOfClass:[UINavigationBar class]]) + { + navigationBar = (UINavigationBar*)superView.superview; + break; + } + + superView = superView.superview; } - superView = superView.superview; - } - - if (navigationBar) - { - CGSize navBarSize = navigationBar.frame.size; - CGFloat superviewCenterX = frame.origin.x + (frame.size.width / 2); - - self.memberAvatarCenterXConstraint.constant = (navBarSize.width / 2) - superviewCenterX; + if (navigationBar) + { + CGSize navBarSize = navigationBar.frame.size; + CGFloat superviewCenterX = frame.origin.x + (frame.size.width / 2); + + self.memberAvatarCenterXConstraint.constant = (navBarSize.width / 2) - superviewCenterX; + } } } + + if (_delegate && [_delegate respondsToSelector:@selector(roomMemberTitleViewDidLayoutSubview:)]) + { + [_delegate roomMemberTitleViewDidLayoutSubview:self]; + } } @end diff --git a/Riot/Views/RoomTitle/RoomAvatarTitleView.m b/Riot/Views/RoomTitle/RoomAvatarTitleView.m index a9a6bb2ea..db361c5be 100644 --- a/Riot/Views/RoomTitle/RoomAvatarTitleView.m +++ b/Riot/Views/RoomTitle/RoomAvatarTitleView.m @@ -40,27 +40,53 @@ if (self.superview) { - // Center horizontally the avatar into the navigation bar - CGRect frame = self.superview.frame; - UINavigationBar *navigationBar; - UIView *superView = self; - while (superView.superview) + if (@available(iOS 11.0, *)) { - if ([superView.superview isKindOfClass:[UINavigationBar class]]) + // Force the title view layout by adding 2 new constraints on the UINavigationBarContentView instance. + NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeTop + multiplier:1.0f + constant:0.0f]; + NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeCenterX + multiplier:1.0f + constant:0.0f]; + + [NSLayoutConstraint activateConstraints:@[topConstraint, centerXConstraint]]; + + // Do not crop the avatar + self.superview.clipsToBounds = NO; + } + else + { + // Center horizontally the avatar into the navigation bar + CGRect frame = self.superview.frame; + UINavigationBar *navigationBar; + UIView *superView = self; + while (superView.superview) { - navigationBar = (UINavigationBar*)superView.superview; - break; + if ([superView.superview isKindOfClass:[UINavigationBar class]]) + { + navigationBar = (UINavigationBar*)superView.superview; + break; + } + + superView = superView.superview; } - superView = superView.superview; - } - - if (navigationBar) - { - CGSize navBarSize = navigationBar.frame.size; - CGFloat superviewCenterX = frame.origin.x + (frame.size.width / 2); - - self.roomAvatarCenterXConstraint.constant = (navBarSize.width / 2) - superviewCenterX; + if (navigationBar) + { + CGSize navBarSize = navigationBar.frame.size; + CGFloat superviewCenterX = frame.origin.x + (frame.size.width / 2); + + self.roomAvatarCenterXConstraint.constant = (navBarSize.width / 2) - superviewCenterX; + } } } } diff --git a/Riot/Views/RoomTitle/RoomTitleView.m b/Riot/Views/RoomTitle/RoomTitleView.m index 2b856af2c..2b7a9f56d 100644 --- a/Riot/Views/RoomTitle/RoomTitleView.m +++ b/Riot/Views/RoomTitle/RoomTitleView.m @@ -73,35 +73,59 @@ if (self.superview) { - // Center horizontally the display name into the navigation bar - CGRect frame = self.superview.frame; - - // Look for the navigation bar. - UINavigationBar *navigationBar; - UIView *superView = self; - while (superView.superview) + if (@available(iOS 11.0, *)) { - if ([superView.superview isKindOfClass:[UINavigationBar class]]) - { - navigationBar = (UINavigationBar*)superView.superview; - break; - } + // Force the title view layout by adding 2 new constraints on the UINavigationBarContentView instance. + NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeTop + multiplier:1.0f + constant:0.0f]; + NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeCenterX + multiplier:1.0f + constant:0.0f]; + + [NSLayoutConstraint activateConstraints:@[topConstraint, centerXConstraint]]; - superView = superView.superview; } - - if (navigationBar) + else { - CGSize navBarSize = navigationBar.frame.size; - CGFloat superviewCenterX = frame.origin.x + (frame.size.width / 2); + // Center horizontally the display name into the navigation bar + CGRect frame = self.superview.frame; - // Check whether the view is not moving away (see navigation between view controllers). - if (superviewCenterX < navBarSize.width) + // Look for the navigation bar. + UINavigationBar *navigationBar; + UIView *superView = self; + while (superView.superview) { - // Center the display name - self.displayNameCenterXConstraint.constant = (navBarSize.width / 2) - superviewCenterX; + if ([superView.superview isKindOfClass:[UINavigationBar class]]) + { + navigationBar = (UINavigationBar*)superView.superview; + break; + } + + superView = superView.superview; } - } + + if (navigationBar) + { + CGSize navBarSize = navigationBar.frame.size; + CGFloat superviewCenterX = frame.origin.x + (frame.size.width / 2); + + // Check whether the view is not moving away (see navigation between view controllers). + if (superviewCenterX < navBarSize.width) + { + // Center the display name + self.displayNameCenterXConstraint.constant = (navBarSize.width / 2) - superviewCenterX; + } + } + } } } diff --git a/Riot/Views/RoomTitle/SimpleRoomTitleView.m b/Riot/Views/RoomTitle/SimpleRoomTitleView.m index 982784428..c143fb194 100644 --- a/Riot/Views/RoomTitle/SimpleRoomTitleView.m +++ b/Riot/Views/RoomTitle/SimpleRoomTitleView.m @@ -38,29 +38,52 @@ if (self.superview) { - // Center horizontally the display name into the navigation bar - CGRect frame = self.superview.frame; - UINavigationBar *navigationBar; - UIView *superView = self; - while (superView.superview) + if (@available(iOS 11.0, *)) { - if ([superView.superview isKindOfClass:[UINavigationBar class]]) + // Force the title view layout by adding 2 new constraints on the UINavigationBarContentView instance. + NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeTop + multiplier:1.0f + constant:0.0f]; + NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:self.superview + attribute:NSLayoutAttributeCenterX + multiplier:1.0f + constant:0.0f]; + + [NSLayoutConstraint activateConstraints:@[topConstraint, centerXConstraint]]; + } + else + { + // Center horizontally the display name into the navigation bar + CGRect frame = self.superview.frame; + UINavigationBar *navigationBar; + UIView *superView = self; + while (superView.superview) { - navigationBar = (UINavigationBar*)superView.superview; - break; + if ([superView.superview isKindOfClass:[UINavigationBar class]]) + { + navigationBar = (UINavigationBar*)superView.superview; + break; + } + + superView = superView.superview; } - superView = superView.superview; + if (navigationBar) + { + CGSize navBarSize = navigationBar.frame.size; + CGFloat superviewCenterX = frame.origin.x + (frame.size.width / 2); + + // Center the display name + self.displayNameCenterXConstraint.constant = (navBarSize.width / 2) - superviewCenterX; + } } - - if (navigationBar) - { - CGSize navBarSize = navigationBar.frame.size; - CGFloat superviewCenterX = frame.origin.x + (frame.size.width / 2); - - // Center the display name - self.displayNameCenterXConstraint.constant = (navBarSize.width / 2) - superviewCenterX; - } } } diff --git a/RiotShareExtension/ViewController/RoomsListViewController.m b/RiotShareExtension/ViewController/RoomsListViewController.m index e83463115..6b38d5066 100644 --- a/RiotShareExtension/ViewController/RoomsListViewController.m +++ b/RiotShareExtension/ViewController/RoomsListViewController.m @@ -253,7 +253,7 @@ { if (!self.recentsSearchBar.isHidden) { - if (!self.recentsSearchBar.text.length && (scrollView.contentOffset.y + scrollView.contentInset.top > self.recentsSearchBar.frame.size.height)) + if (!self.recentsSearchBar.text.length && (scrollView.contentOffset.y + scrollView.mxk_adjustedContentInset.top > self.recentsSearchBar.frame.size.height)) { // Hide the search bar [self hideSearchBar:YES];