Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
Weblate 2017-10-03 10:20:35 +00:00
commit 66685b2deb
57 changed files with 1756 additions and 691 deletions

View file

@ -26,4 +26,7 @@ Denis Morozov <dmorozkn at gmail.com>
Aram Sargsyan <aram.sargsyan.1997 at gmail.com>
* PR #1341: Read receipts details
Vivian Lim <vivvnlim at gmail.com>
* PR #1513 Return key on hardware keyboards now sends messages

View file

@ -22,7 +22,7 @@ pod 'MatrixKit', '0.6.2'
#pod 'MatrixKit', :path => '../matrix-ios-kit/MatrixKit.podspec'
#pod 'MatrixSDK', :path => '../matrix-ios-sdk/MatrixSDK.podspec'
pod 'GBDeviceInfo', '~> 4.3.0'
pod 'GBDeviceInfo', '~> 4.4.0'
pod 'GoogleAnalytics'
@ -32,7 +32,7 @@ pod 'WebRTC', '58.17.16937'
# OLMKit for crypto
pod 'OLMKit'
#pod 'OLMKit', :path => '../olm/OLMKit.podspec'
pod 'Realm', '~> 2.8.1'
pod 'Realm', '~> 2.10.2'
# Remove warnings from "bad" pods
pod 'OLMKit', :inhibit_warnings => true
@ -50,7 +50,7 @@ pod 'WebRTC', '58.17.16937'
# OLMKit for crypto
pod 'OLMKit'
#pod 'OLMKit', :path => '../olm/OLMKit.podspec'
pod 'Realm', '~> 2.8.1'
pod 'Realm', '~> 2.10.2'
# The tagged version on which this version of Riot share extension has been built
pod 'MatrixKit/AppExtension', '0.6.2'

View file

@ -15,18 +15,18 @@ PODS:
- AFNetworking/UIKit (3.1.0):
- AFNetworking/NSURLSession
- cmark (0.24.1)
- DTCoreText (1.6.20):
- DTCoreText/Core (= 1.6.20)
- DTCoreText (1.6.21):
- DTCoreText/Core (= 1.6.21)
- DTFoundation/Core (~> 1.7.5)
- DTFoundation/DTAnimatedGIF (~> 1.7.5)
- DTFoundation/DTHTMLParser (~> 1.7.5)
- DTFoundation/UIKit (~> 1.7.5)
- DTCoreText/Core (1.6.20):
- DTCoreText/Core (1.6.21):
- DTFoundation/Core (~> 1.7.5)
- DTFoundation/DTAnimatedGIF (~> 1.7.5)
- DTFoundation/DTHTMLParser (~> 1.7.5)
- DTFoundation/UIKit (~> 1.7.5)
- DTCoreText/Extension (1.6.20):
- DTCoreText/Extension (1.6.21):
- DTFoundation/Core (~> 1.7.5)
- DTFoundation/DTAnimatedGIF (~> 1.7.5)
- DTFoundation/DTHTMLParser (~> 1.7.5)
@ -91,7 +91,7 @@ DEPENDENCIES:
SPEC CHECKSUMS:
AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67
cmark: ec0275215b504780287b6fca360224e384368af8
DTCoreText: 51904f2374af443e0d270d6fdc76035ab6f9ef8a
DTCoreText: e5d688cffc9f6a61eddd1a4f94e2046851230de3
DTFoundation: 26a164ef510877387906fb92bff524a792db4bf8
GBDeviceInfo: caae36532afcc209b51ac62bba547aadab9e88f2
GoogleAnalytics: f42cc53a87a51fe94334821868d9c8481ff47a7b

View file

@ -10,13 +10,15 @@
05D592A32FF1D1877B89F73C /* libPods-Riot.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FD9D0BDE9232898950554DD5 /* libPods-Riot.a */; };
2435179C1F375B9400D0683E /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 2466B7551F2F80B800AE27B0 /* Info.plist */; };
2435179F1F375C0F00D0683E /* Vector.strings in Resources */ = {isa = PBXBuildFile; fileRef = 327382C01F276AED00356143 /* Vector.strings */; };
2439DD621F6BBE760090F42D /* RecentRoomTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 2439DD611F6BBE760090F42D /* RecentRoomTableViewCell.m */; };
2439DD641F6BBEA50090F42D /* RecentRoomTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2439DD631F6BBEA50090F42D /* RecentRoomTableViewCell.xib */; };
245FC3ED1F3D079A00603C6A /* ShareExtensionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 245FC3EB1F3CAF9800603C6A /* ShareExtensionManager.m */; };
245FC3EF1F3DD30800603C6A /* RecentCellData.m in Sources */ = {isa = PBXBuildFile; fileRef = F083BC071E7009EC00A9B29C /* RecentCellData.m */; };
2466B73E1F2DFAC100AE27B0 /* animatedLogo-4.png in Resources */ = {isa = PBXBuildFile; fileRef = F083BB201E7009EC00A9B29C /* animatedLogo-4.png */; };
24B5103E1EFA7083004C6AD2 /* ReadReceiptsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 24B5103D1EFA7083004C6AD2 /* ReadReceiptsViewController.m */; };
24B510401EFA88CC004C6AD2 /* ReadReceiptsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 24B5103F1EFA88CC004C6AD2 /* ReadReceiptsViewController.xib */; };
24CBEC591F0EAD310093EABB /* RiotShareExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 24CBEC4E1F0EAD310093EABB /* RiotShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
24D6B3581F3C90D300FC7A71 /* ShareRecentsDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 24D6B3571F3C90D300FC7A71 /* ShareRecentsDataSource.m */; };
24D6B3581F3C90D300FC7A71 /* ShareDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 24D6B3571F3C90D300FC7A71 /* ShareDataSource.m */; };
24D6B3591F3CA02900FC7A71 /* SharePresentingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 24D6B34B1F3C8F8A00FC7A71 /* SharePresentingViewController.m */; };
24D6B35A1F3CA02C00FC7A71 /* ShareViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 24D6B34D1F3C8F8A00FC7A71 /* ShareViewController.m */; };
24D6B35B1F3CA03300FC7A71 /* ShareViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 24D6B34E1F3C8F8A00FC7A71 /* ShareViewController.xib */; };
@ -27,7 +29,7 @@
24EEE5A11F23A09A00B3C705 /* RiotDesignValues.m in Sources */ = {isa = PBXBuildFile; fileRef = F083BC171E7009EC00A9B29C /* RiotDesignValues.m */; };
24EEE5A21F23A8B400B3C705 /* MXRoom+Riot.m in Sources */ = {isa = PBXBuildFile; fileRef = F083BBE81E7009EC00A9B29C /* MXRoom+Riot.m */; };
24EEE5A31F23A8C300B3C705 /* AvatarGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = F083BC111E7009EC00A9B29C /* AvatarGenerator.m */; };
24EEE5A41F24C06E00B3C705 /* (null) in Resources */ = {isa = PBXBuildFile; };
24EEE5A41F24C06E00B3C705 /* BuildFile in Resources */ = {isa = PBXBuildFile; };
24EEE5A81F25529600B3C705 /* cancel@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = F0614A121EDEE65000F5DC9A /* cancel@3x.png */; };
24EEE5A91F25529900B3C705 /* cancel@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F0614A111EDEE65000F5DC9A /* cancel@2x.png */; };
24EEE5AA1F25529C00B3C705 /* cancel.png in Resources */ = {isa = PBXBuildFile; fileRef = F0614A101EDEE65000F5DC9A /* cancel.png */; };
@ -73,6 +75,7 @@
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 */; };
@ -81,6 +84,7 @@
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 */; };
@ -490,6 +494,10 @@
F0A4A1671EF7CB66003630DB /* members_list_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = F0A4A1641EF7CB66003630DB /* members_list_icon.png */; };
F0A4A1681EF7CB66003630DB /* members_list_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F0A4A1651EF7CB66003630DB /* members_list_icon@2x.png */; };
F0A4A1691EF7CB66003630DB /* members_list_icon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = F0A4A1661EF7CB66003630DB /* members_list_icon@3x.png */; };
F0A8955F1F7D1FEA00BD6C2A /* MXRoomSummary+Riot.m in Sources */ = {isa = PBXBuildFile; fileRef = F0D2ADA01F6AA5FD00A7097D /* MXRoomSummary+Riot.m */; };
F0A895601F7D404B00BD6C2A /* e2e_verified.png in Resources */ = {isa = PBXBuildFile; fileRef = F083BB681E7009EC00A9B29C /* e2e_verified.png */; };
F0A895611F7D404E00BD6C2A /* e2e_verified@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F083BB691E7009EC00A9B29C /* e2e_verified@2x.png */; };
F0A895621F7D405000BD6C2A /* e2e_verified@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = F083BB6A1E7009EC00A9B29C /* e2e_verified@3x.png */; };
F0B4CBA51F418D0B008E99C5 /* WebViewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F0B4CBA41F418D0B008E99C5 /* WebViewViewController.m */; };
F0B4CBA71F41CA44008E99C5 /* DeviceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F0B4CBA61F41CA44008E99C5 /* DeviceView.xib */; };
F0B4CBAA1F41E71A008E99C5 /* RiotNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = F0B4CBA91F41E71A008E99C5 /* RiotNavigationController.m */; };
@ -578,6 +586,9 @@
/* Begin PBXFileReference section */
12AA0005C8B3D8D8162584C5 /* Pods-RiotShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotShareExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RiotShareExtension/Pods-RiotShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
2439DD601F6BBE760090F42D /* RecentRoomTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecentRoomTableViewCell.h; sourceTree = "<group>"; };
2439DD611F6BBE760090F42D /* RecentRoomTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RecentRoomTableViewCell.m; sourceTree = "<group>"; };
2439DD631F6BBEA50090F42D /* RecentRoomTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RecentRoomTableViewCell.xib; sourceTree = "<group>"; };
245FC3EA1F3CAF9800603C6A /* ShareExtensionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShareExtensionManager.h; sourceTree = "<group>"; };
245FC3EB1F3CAF9800603C6A /* ShareExtensionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShareExtensionManager.m; sourceTree = "<group>"; };
2466B7551F2F80B800AE27B0 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = RiotShareExtension/Info.plist; sourceTree = SOURCE_ROOT; };
@ -596,8 +607,8 @@
24D6B34C1F3C8F8A00FC7A71 /* ShareViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShareViewController.h; sourceTree = "<group>"; };
24D6B34D1F3C8F8A00FC7A71 /* ShareViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShareViewController.m; sourceTree = "<group>"; };
24D6B34E1F3C8F8A00FC7A71 /* ShareViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShareViewController.xib; sourceTree = "<group>"; };
24D6B3561F3C90D300FC7A71 /* ShareRecentsDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShareRecentsDataSource.h; sourceTree = "<group>"; };
24D6B3571F3C90D300FC7A71 /* ShareRecentsDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShareRecentsDataSource.m; sourceTree = "<group>"; };
24D6B3561F3C90D300FC7A71 /* ShareDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShareDataSource.h; sourceTree = "<group>"; };
24D6B3571F3C90D300FC7A71 /* ShareDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShareDataSource.m; sourceTree = "<group>"; };
3205ED7B1E976C8A003D65FA /* DirectoryServerPickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectoryServerPickerViewController.h; sourceTree = "<group>"; };
3205ED7C1E976C8A003D65FA /* DirectoryServerPickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DirectoryServerPickerViewController.m; sourceTree = "<group>"; };
3205ED811E97725E003D65FA /* DirectoryServerTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectoryServerTableViewCell.h; sourceTree = "<group>"; };
@ -649,6 +660,8 @@
32AE61ED1F0D2183007255F4 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = InfoPlist.strings; sourceTree = "<group>"; };
32AE61EF1F0D2183007255F4 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = Localizable.strings; sourceTree = "<group>"; };
32AE61F11F0D2183007255F4 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = Vector.strings; sourceTree = "<group>"; };
32C2356D1F7B871800E38FC5 /* WidgetPickerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WidgetPickerViewController.h; sourceTree = "<group>"; };
32C2356E1F7B871800E38FC5 /* WidgetPickerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WidgetPickerViewController.m; sourceTree = "<group>"; };
32D392151EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectoryServerDetailTableViewCell.h; sourceTree = "<group>"; };
32D392161EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DirectoryServerDetailTableViewCell.m; sourceTree = "<group>"; };
32D392171EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DirectoryServerDetailTableViewCell.xib; sourceTree = "<group>"; };
@ -662,6 +675,7 @@
32FD0A3C1EB0CD9B0072B066 /* BugReportViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BugReportViewController.xib; sourceTree = "<group>"; };
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 = "<group>"; };
83711A7B1F6F8E7D008F0D4D /* KeyboardGrowingTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KeyboardGrowingTextView.m; sourceTree = "<group>"; };
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 = "<group>"; };
92324BE11F4F66D3009DE194 /* IncomingCallView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IncomingCallView.h; sourceTree = "<group>"; };
92324BE21F4F66D3009DE194 /* IncomingCallView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IncomingCallView.m; sourceTree = "<group>"; };
@ -1275,12 +1289,24 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
2439DD5F1F6BBE390090F42D /* Views */ = {
isa = PBXGroup;
children = (
2439DD601F6BBE760090F42D /* RecentRoomTableViewCell.h */,
2439DD611F6BBE760090F42D /* RecentRoomTableViewCell.m */,
2439DD631F6BBEA50090F42D /* RecentRoomTableViewCell.xib */,
);
name = Views;
path = RiotShareExtension/Views;
sourceTree = SOURCE_ROOT;
};
24CBEC4F1F0EAD310093EABB /* RiotShareExtension */ = {
isa = PBXGroup;
children = (
2466B7551F2F80B800AE27B0 /* Info.plist */,
2466B7561F2F80B800AE27B0 /* RiotShareExtension.entitlements */,
24D6B3441F3C8F8A00FC7A71 /* ViewController */,
2439DD5F1F6BBE390090F42D /* Views */,
24D6B3551F3C8FCC00FC7A71 /* Model */,
);
name = RiotShareExtension;
@ -1311,8 +1337,8 @@
children = (
245FC3EA1F3CAF9800603C6A /* ShareExtensionManager.h */,
245FC3EB1F3CAF9800603C6A /* ShareExtensionManager.m */,
24D6B3561F3C90D300FC7A71 /* ShareRecentsDataSource.h */,
24D6B3571F3C90D300FC7A71 /* ShareRecentsDataSource.m */,
24D6B3561F3C90D300FC7A71 /* ShareDataSource.h */,
24D6B3571F3C90D300FC7A71 /* ShareDataSource.m */,
);
name = Model;
path = RiotShareExtension/Model;
@ -1349,6 +1375,8 @@
3233F72E1F31F4BF006ACA81 /* JitsiViewController.h */,
3233F72F1F31F4BF006ACA81 /* JitsiViewController.m */,
3233F7301F31F4BF006ACA81 /* JitsiViewController.xib */,
32C2356D1F7B871800E38FC5 /* WidgetPickerViewController.h */,
32C2356E1F7B871800E38FC5 /* WidgetPickerViewController.m */,
);
path = Widgets;
sourceTree = "<group>";
@ -2201,6 +2229,7 @@
F083BCD91E7009EC00A9B29C /* RoomInputToolbarView.h */,
F083BCDA1E7009EC00A9B29C /* RoomInputToolbarView.m */,
F083BCDB1E7009EC00A9B29C /* RoomInputToolbarView.xib */,
83711A7B1F6F8E7D008F0D4D /* KeyboardGrowingTextView.m */,
);
path = RoomInputToolbar;
sourceTree = "<group>";
@ -2487,14 +2516,19 @@
24EEE5B51F2607C500B3C705 /* SegmentedViewController.xib in Resources */,
24EEE5A91F25529900B3C705 /* cancel@2x.png in Resources */,
F0B7A8B11F475783006E27D2 /* RoomsListViewController.xib in Resources */,
F0A895621F7D405000BD6C2A /* e2e_verified@3x.png in Resources */,
2435179C1F375B9400D0683E /* Info.plist in Resources */,
24EEE5A81F25529600B3C705 /* cancel@3x.png in Resources */,
2466B73E1F2DFAC100AE27B0 /* animatedLogo-4.png in Resources */,
2435179F1F375C0F00D0683E /* Vector.strings in Resources */,
24EEE5AF1F25F0F500B3C705 /* Images.xcassets in Resources */,
F0A895611F7D404E00BD6C2A /* e2e_verified@2x.png in Resources */,
24EEE5AA1F25529C00B3C705 /* cancel.png in Resources */,
F0A895601F7D404B00BD6C2A /* e2e_verified.png in Resources */,
24D6B35E1F3CA03E00FC7A71 /* FallbackViewController.xib in Resources */,
24EEE5A41F24C06E00B3C705 /* (null) in Resources */,
24EEE5A41F24C06E00B3C705 /* BuildFile in Resources */,
2439DD641F6BBEA50090F42D /* RecentRoomTableViewCell.xib in Resources */,
24EEE5A41F24C06E00B3C705 /* BuildFile in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -3025,7 +3059,7 @@
buildActionMask = 2147483647;
files = (
24EEE5A01F23A08900B3C705 /* RoomTableViewCell.m in Sources */,
24D6B3581F3C90D300FC7A71 /* ShareRecentsDataSource.m in Sources */,
24D6B3581F3C90D300FC7A71 /* ShareDataSource.m in Sources */,
245FC3EF1F3DD30800603C6A /* RecentCellData.m in Sources */,
24D6B35C1F3CA03600FC7A71 /* RoomsListViewController.m in Sources */,
24D6B35A1F3CA02C00FC7A71 /* ShareViewController.m in Sources */,
@ -3034,6 +3068,8 @@
24D6B35D1F3CA03A00FC7A71 /* FallbackViewController.m in Sources */,
24EEE5A11F23A09A00B3C705 /* RiotDesignValues.m in Sources */,
24EEE5A21F23A8B400B3C705 /* MXRoom+Riot.m in Sources */,
F0A8955F1F7D1FEA00BD6C2A /* MXRoomSummary+Riot.m in Sources */,
2439DD621F6BBE760090F42D /* RecentRoomTableViewCell.m in Sources */,
245FC3ED1F3D079A00603C6A /* ShareExtensionManager.m in Sources */,
24EEE5B41F2607C000B3C705 /* SegmentedViewController.m in Sources */,
);
@ -3075,6 +3111,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 */,
@ -3101,6 +3138,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 */,

View file

@ -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];
}

View file

@ -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.";

View file

@ -106,7 +106,6 @@
"auth_reset_password_error_not_found" = "Your email address does not appear to be associated with a Matrix ID on this Homeserver.";
"auth_reset_password_success_message" = "Your password has been reset.\n\nYou have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, re-log in on each device.";
"auth_add_email_and_phone_warning" = "Registration with email and phone number at once is not supported yet until the api exists. Only the phone number will be taken into account. You may add your email to your profile in settings.";
"auth_share_extension_prompt" = "Login in the main app to share content";
// Chat creation
"room_creation_title" = "New Chat";
@ -340,8 +339,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 %@?";
@ -456,6 +459,8 @@
// Events formatter
"event_formatter_member_updates" = "%tu membership changes";
"event_formatter_widget_added" = "%@ widget added by %@";
"event_formatter_widget_removed" = "%@ widget removed by %@";
"event_formatter_jitsi_widget_added" = "VoIP conference added by %@";
"event_formatter_jitsi_widget_removed" = "VoIP conference removed by %@";
@ -525,3 +530,7 @@
"widget_integration_missing_user_id" = "Missing user_id in request.";
"widget_integration_room_not_visible" = "Room %@ is not visible.";
// Share extension
"share_extension_auth_prompt" = "Login in the main app to share content";
"share_extension_failed_to_encrypt" = "Failed to send. Check in the main app the encryption settings for this room";

View file

@ -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";

View file

@ -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";

View file

@ -417,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";

View file

@ -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";

View file

@ -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 */

View file

@ -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" = "Не удалось создать виджет";

View file

@ -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" = "关闭";

View file

@ -46,25 +46,52 @@
{
NSString *displayText;
// Prepare the display name of the sender
NSString *senderDisplayName = roomState ? [self senderDisplayNameForEvent:event withRoomState:roomState] : event.sender;
Widget *widget = [[Widget alloc] initWithWidgetEvent:event inMatrixSession:mxSession];
if (widget)
{
// Prepare the display name of the sender
NSString *senderDisplayName = roomState ? [self senderDisplayNameForEvent:event withRoomState:roomState] : event.sender;
if ([event.content[@"type"] isEqualToString:kWidgetTypeJitsi])
{
// This is an alive jitsi widget
displayText = [NSString stringWithFormat:NSLocalizedStringFromTable(@"event_formatter_jitsi_widget_added", @"Vector", nil), senderDisplayName];
}
else if (event.content.count == 0)
{
// This is a closed widget
// Check if it corresponds to a jitsi widget by looking at other state events for
// this jitsi widget (widget id = event.stateKey).
for (MXEvent *widgetStateEvent in [roomState stateEventsWithType:kWidgetEventTypeString])
if (widget.isActive)
{
if ([widgetStateEvent.stateKey isEqualToString:event.stateKey] && [widgetStateEvent.content[@"type"] isEqualToString:kWidgetTypeJitsi])
if ([widget.type isEqualToString:kWidgetTypeJitsi])
{
displayText = [NSString stringWithFormat:NSLocalizedStringFromTable(@"event_formatter_jitsi_widget_removed", @"Vector", nil), senderDisplayName];
break;
// This is an alive jitsi widget
displayText = [NSString stringWithFormat:NSLocalizedStringFromTable(@"event_formatter_jitsi_widget_added", @"Vector", nil), senderDisplayName];
}
else
{
displayText = [NSString stringWithFormat:NSLocalizedStringFromTable(@"event_formatter_widget_added", @"Vector", nil),
widget.name ? widget.name : widget.type,
senderDisplayName];
}
}
else
{
// This is a closed widget
// Check if it corresponds to a jitsi widget by looking at other state events for
// this jitsi widget (widget id = event.stateKey).
for (MXEvent *widgetStateEvent in [roomState stateEventsWithType:kWidgetEventTypeString])
{
if ([widgetStateEvent.stateKey isEqualToString:widget.widgetId])
{
Widget *activeWidget = [[Widget alloc] initWithWidgetEvent:widgetStateEvent inMatrixSession:mxSession];
if (activeWidget.isActive)
{
if ([activeWidget.type isEqualToString:kWidgetTypeJitsi])
{
// This was a jitsi widget
displayText = [NSString stringWithFormat:NSLocalizedStringFromTable(@"event_formatter_jitsi_widget_removed", @"Vector", nil), senderDisplayName];
}
else
{
displayText = [NSString stringWithFormat:NSLocalizedStringFromTable(@"event_formatter_widget_removed", @"Vector", nil),
activeWidget.name ? activeWidget.name : activeWidget.type,
senderDisplayName];
}
break;
}
}
}
}
}

View file

@ -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"])

View file

@ -41,22 +41,25 @@
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);
// 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);
}
}
}

View file

@ -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<Widget*> *)widgetsOfTypes:(NSArray<NSString*>*)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<Widget*> *)widgetsNotOfTypes:(NSArray<NSString*>*)notWidgetTypes inRoom:(MXRoom*)room;
/**
Add a modular widget to a room.

View file

@ -87,7 +87,17 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
return [self widgetsOfTypes:nil inRoom:room];
}
- (NSArray<Widget *> *)widgetsOfTypes:(NSArray<NSString *> *)widgetTypes inRoom:(MXRoom *)room
- (NSArray<Widget*> *)widgetsOfTypes:(NSArray<NSString*>*)widgetTypes inRoom:(MXRoom*)room;
{
return [self widgetsOfTypes:widgetTypes butNotTypesOf:nil inRoom:room];
}
- (NSArray<Widget*> *)widgetsNotOfTypes:(NSArray<NSString*>*)notWidgetTypes inRoom:(MXRoom*)room
{
return [self widgetsOfTypes:nil butNotTypesOf:notWidgetTypes inRoom:room];
}
- (NSArray<Widget*> *)widgetsOfTypes:(NSArray<NSString*>*)widgetTypes butNotTypesOf:(NSArray<NSString*>*)notWidgetTypes inRoom:(MXRoom*)room;
{
// Widget id -> widget
NSMutableDictionary <NSString*, Widget *> *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;
}
}
}

View file

@ -35,7 +35,7 @@
#define TABLEVIEW_SECTION_HEADER_HEIGHT 28
#define TABLEVIEW_SECTION_HEADER_HEIGHT_WHEN_HIDDEN 0.01f
@interface ContactDetailsViewController ()
@interface ContactDetailsViewController () <RoomMemberTitleViewDelegate>
{
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

View file

@ -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

View file

@ -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];
}];

View file

@ -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];
}];

View file

@ -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;

View file

@ -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

View file

@ -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];
}];
}

View file

@ -33,7 +33,7 @@
#define TABLEVIEW_SECTION_HEADER_HEIGHT 28
#define TABLEVIEW_SECTION_HEADER_HEIGHT_WHEN_HIDDEN 0.01f
@interface RoomMemberDetailsViewController ()
@interface RoomMemberDetailsViewController () <RoomMemberTitleViewDelegate>
{
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

View file

@ -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];
}];
}

View file

@ -278,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];
}];
}

View file

@ -110,7 +110,7 @@
#import "MXRoom+Riot.h"
#import "IntegrationManagerViewController.h"
#import "WidgetViewController.h"
#import "WidgetPickerViewController.h"
@interface RoomViewController ()
{
@ -374,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];
@ -399,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];
@ -491,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];
}];
}
@ -683,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
@ -710,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];
@ -1189,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)
{
@ -1366,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)
{
@ -1482,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];
@ -1573,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)
@ -2881,19 +2907,16 @@
// Matrix Apps button
else if (self.navigationItem.rightBarButtonItems.count == 2 && sender == self.navigationItem.rightBarButtonItems[1])
{
// Temporary code to test `WidgetViewController`
// TODO: remove it
// NSArray<Widget *> *widgets = [[WidgetManager sharedManager] widgetsInRoom:self.roomDataSource.room];
// if (widgets.count)
// {
// // Hide back button title
// self.navigationItem.backBarButtonItem =[[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
//
// WidgetViewController *widgetVC = [[WidgetViewController alloc] initForWidget:widgets[0]];
// [self.navigationController pushViewController:widgetVC animated:YES];
// }
// else
if (self.widgetsCount)
{
WidgetPickerViewController *widgetPicker = [[WidgetPickerViewController alloc] initForMXSession:self.roomDataSource.mxSession
inRoom:self.roomDataSource.roomId];
[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
@ -2993,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];
@ -3445,6 +3468,7 @@
// Update the bar
[self refreshActivitiesViewDisplay];
[self refreshRoomInputToolbar];
[self refreshRoomTitle];
}
}];
}
@ -3465,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
@ -3700,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)
{
@ -3724,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
@ -4075,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
@ -4162,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);
}

View file

@ -156,34 +156,47 @@
NSArray<NSString*> *roomDirectoryServers = [[NSUserDefaults standardUserDefaults] objectForKey:@"roomDirectoryServers"];
directoryServersDataSource.roomDirectoryServers = roomDirectoryServers;
__weak typeof(self) weakSelf = self;
[directoryServerPickerViewController displayWithDataSource:directoryServersDataSource onComplete:^(id<MXKDirectoryServerCellDataStoring> 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];
}
}];
}
}];

View file

@ -19,9 +19,8 @@
#import "DeviceView.h"
#import "MediaPickerViewController.h"
#import "TableViewCellWithCheckBoxes.h"
@interface SettingsViewController : MXKTableViewController<UITextFieldDelegate, MediaPickerViewControllerDelegate, MXKDeviceViewDelegate, UIDocumentInteractionControllerDelegate, MXKCountryPickerViewControllerDelegate, MXKLanguagePickerViewControllerDelegate, TableViewCellWithCheckBoxesDelegate>
@interface SettingsViewController : MXKTableViewController<UITextFieldDelegate, MediaPickerViewControllerDelegate, MXKDeviceViewDelegate, UIDocumentInteractionControllerDelegate, MXKCountryPickerViewControllerDelegate, MXKLanguagePickerViewControllerDelegate>
@end

View file

@ -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

View file

@ -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 <UIKit/UIKit.h>
#import <MatrixSDK/MatrixSDK.h>
#import <MatrixKit/MatrixKit.h>
/**
`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

View file

@ -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<Widget*> *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

View file

@ -20,6 +20,8 @@
- (void)prepareForReuse
{
[super prepareForReuse];
if (self.heightConstraint != 0)
{
self.heightConstraint = 0;

View file

@ -45,6 +45,8 @@
- (void)prepareForReuse
{
[super prepareForReuse];
if (self.pictureViewTopConstraint.constant != xibPictureViewTopConstraintConstant)
{
self.pictureViewTopConstraint.constant = xibPictureViewTopConstraintConstant;

View file

@ -45,6 +45,8 @@
- (void)prepareForReuse
{
[super prepareForReuse];
// Reset avatars
for (UIView *avatarView in self.avatarsView.subviews)
{

View file

@ -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 <Foundation/Foundation.h>
#import <HPGrowingTextView.h>
#import "RoomInputToolbarView.h"
@interface KeyboardGrowingTextView: HPGrowingTextView
- (NSArray<UIKeyCommand *> *)keyCommands;
@end
@implementation KeyboardGrowingTextView
- (NSArray<UIKeyCommand *> *)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

View file

@ -46,7 +46,7 @@
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QWp-NV-uh5" userLabel="Message Composer Container">
<rect key="frame" x="62" y="4" width="443" height="38"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wgb-ON-N29" customClass="HPGrowingTextView">
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wgb-ON-N29" customClass="KeyboardGrowingTextView">
<rect key="frame" x="0.0" y="0.0" width="443" height="38"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="GrowingTextView"/>

View file

@ -16,6 +16,20 @@
#import <MatrixKit/MatrixKit.h>
// We add here a protocol to handle title view layout update.
@class RoomMemberTitleView;
@protocol RoomMemberTitleViewDelegate <NSObject>
@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<RoomMemberTitleViewDelegate> delegate;
@end

View file

@ -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

View file

@ -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;
}
}
}
}

View file

@ -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;
}
}
}
}
}

View file

@ -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;
}
}
}

View file

@ -0,0 +1,38 @@
/*
Copyright 2017 Aram Sargsyan
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 <MatrixKit/MatrixKit.h>
typedef NS_ENUM(NSInteger, ShareDataSourceMode)
{
DataSourceModePeople,
DataSourceModeRooms
};
@interface ShareDataSource : MXKRecentsDataSource
- (instancetype)initWithMode:(ShareDataSourceMode)dataSourceMode;
/**
Returns the cell data at the index path
@param indexPath the index of the cell
@return the MXKRecentCellData instance if it exists
*/
- (MXKRecentCellData *)cellDataAtIndexPath:(NSIndexPath *)indexPath;
@end

View file

@ -0,0 +1,174 @@
/*
Copyright 2017 Aram Sargsyan
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 "ShareDataSource.h"
#import "ShareExtensionManager.h"
#import "RecentRoomTableViewCell.h"
@interface ShareDataSource ()
@property (nonatomic, readwrite) ShareDataSourceMode dataSourceMode;
@property NSArray <MXKRecentCellData *> *recentCellDatas;
@property NSMutableArray <MXKRecentCellData *> *visibleRoomCellDatas;
@end
@implementation ShareDataSource
- (instancetype)initWithMode:(ShareDataSourceMode)dataSourceMode
{
self = [super init];
if (self)
{
self.dataSourceMode = dataSourceMode;
[self loadCellData];
}
return self;
}
- (void)destroy
{
[super destroy];
_recentCellDatas = nil;
_visibleRoomCellDatas = nil;
}
#pragma mark - Private
- (void)loadCellData
{
[[ShareExtensionManager sharedManager].fileStore asyncRoomsSummaries:^(NSArray<MXRoomSummary *> * _Nonnull roomsSummaries) {
NSMutableArray *cellData = [NSMutableArray array];
for (MXRoomSummary *roomSummary in roomsSummaries)
{
MXKRecentCellData *recentCellData = [[MXKRecentCellData alloc] initWithRoomSummary:roomSummary andRecentListDataSource:nil];
if ((self.dataSourceMode == DataSourceModeRooms) ^ roomSummary.isDirect)
{
[cellData addObject:recentCellData];
}
}
// Sort rooms according to their last messages (most recent first)
NSComparator comparator = ^NSComparisonResult(MXKRecentCellData *recentCellData1, MXKRecentCellData *recentCellData2) {
NSComparisonResult result = NSOrderedAscending;
if (recentCellData2.roomSummary.lastMessageOriginServerTs > recentCellData1.roomSummary.lastMessageOriginServerTs)
{
result = NSOrderedDescending;
}
else if (recentCellData2.roomSummary.lastMessageOriginServerTs == recentCellData1.roomSummary.lastMessageOriginServerTs)
{
result = NSOrderedSame;
}
return result;
};
[cellData sortUsingComparator:comparator];
self.recentCellDatas = cellData;
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate dataSource:self didCellChange:nil];
});
} failure:^(NSError * _Nonnull error) {
NSLog(@"[ShareDataSource failed to get room summaries]");
}];
}
#pragma mark - MXKRecentsDataSource
- (MXKRecentCellData *)cellDataAtIndexPath:(NSIndexPath *)indexPath
{
if (self.visibleRoomCellDatas)
{
return self.visibleRoomCellDatas[indexPath.row];
}
return self.recentCellDatas[indexPath.row];
}
- (void)searchWithPatterns:(NSArray *)patternsList
{
if (self.visibleRoomCellDatas)
{
[self.visibleRoomCellDatas removeAllObjects];
}
else
{
self.visibleRoomCellDatas = [NSMutableArray arrayWithCapacity:self.recentCellDatas.count];
}
if (patternsList.count)
{
for (MXKRecentCellData *cellData in self.recentCellDatas)
{
for (NSString* pattern in patternsList)
{
if (cellData.roomSummary.displayname && [cellData.roomSummary.displayname rangeOfString:pattern options:NSCaseInsensitiveSearch].location != NSNotFound)
{
[self.visibleRoomCellDatas addObject:cellData];
break;
}
}
}
}
else
{
self.visibleRoomCellDatas = nil;
}
[self.delegate dataSource:self didCellChange:nil];
}
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.visibleRoomCellDatas)
{
return self.visibleRoomCellDatas.count;
}
return self.recentCellDatas.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
RecentRoomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[RecentRoomTableViewCell defaultReuseIdentifier]];
[cell render:[self cellDataAtIndexPath:indexPath]];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
@end

View file

@ -21,10 +21,11 @@
@class SharePresentingViewController;
/**
Posted when the matrix session has been changed.
The notification object is the matrix session.
Posted when the matrix user account and his data has been checked and updated.
The notification object is the MXKAccount instance.
*/
extern NSString *const kShareExtensionManagerDidChangeMXSessionNotification;
extern NSString *const kShareExtensionManagerDidUpdateAccountDataNotification;
/**
The protocol for the manager's delegate
@ -42,6 +43,11 @@ extern NSString *const kShareExtensionManagerDidChangeMXSessionNotification;
@optional
/**
Called when the manager starts sending the content to a room
@param extensionManager the ShareExtensionManager object that called the method
@param room the room where content will be sent
*/
- (void)shareExtensionManager:(ShareExtensionManager *)extensionManager didStartSendingContentToRoom:(MXRoom *)room;
/**
@ -71,9 +77,14 @@ extern NSString *const kShareExtensionManagerDidChangeMXSessionNotification;
@property (nonatomic) SharePresentingViewController *primaryViewController;
/**
The associated matrix session (nil by default).
The current user account
*/
@property (nonatomic, readonly) MXSession *mxSession;
@property (nonatomic, readonly) MXKAccount *userAccount;
/**
The shared file store
*/
@property (nonatomic, readonly) MXFileStore *fileStore;
/**
A delegate used to notify about needed UI changes when sharing
@ -89,9 +100,9 @@ extern NSString *const kShareExtensionManagerDidChangeMXSessionNotification;
Send the content that the user has chosen to a room
@param room the room to send the content to
@param failureBlock the code to be executed when sharing has failed for whatever reason
note: there is no "successBlock" parameter because when the sharing succeds, the extension needs to close itself
note: there is no "successBlock" parameter because when the sharing succeeds, the extension needs to close itself
*/
- (void)sendContentToRoom:(MXRoom *)room failureBlock:(void(^)())failureBlock;
- (void)sendContentToRoom:(MXRoom *)room failureBlock:(void(^)(NSError *error))failureBlock;
/**
Checks if there is an image in the user chosen content
@ -106,3 +117,10 @@ extern NSString *const kShareExtensionManagerDidChangeMXSessionNotification;
- (void)terminateExtensionCanceled:(BOOL)canceled;
@end
@interface NSItemProvider (ShareExtensionManager)
@property BOOL isLoaded;
@end

View file

@ -18,8 +18,9 @@
#import "SharePresentingViewController.h"
#import "MXKPieChartHUD.h"
@import MobileCoreServices;
#import "objc/runtime.h"
NSString *const kShareExtensionManagerDidChangeMXSessionNotification = @"kShareExtensionManagerDidChangeMXSessionNotification";
NSString *const kShareExtensionManagerDidUpdateAccountDataNotification = @"kShareExtensionManagerDidUpdateAccountDataNotification";
typedef NS_ENUM(NSInteger, ImageCompressionMode)
{
@ -29,13 +30,15 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
ImageCompressionModeLarge
};
@interface ShareExtensionManager ()
// The current user account
@property (nonatomic) MXKAccount *userAccount;
@property (nonatomic, readwrite) MXKAccount *userAccount;
@property ImageCompressionMode imageCompressionMode;
@property CGFloat actualLargeSize;
@property (nonatomic) NSMutableArray <NSData *> *pendingImages;
@property (nonatomic) NSMutableDictionary <NSString *, NSNumber *> *imageUploadProgresses;
@property (nonatomic) ImageCompressionMode imageCompressionMode;
@property (nonatomic) CGFloat actualLargeSize;
@end
@ -52,6 +55,9 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
sharedInstance = [[self alloc] init];
sharedInstance.pendingImages = [NSMutableArray array];
sharedInstance.imageUploadProgresses = [NSMutableDictionary dictionary];
[[NSNotificationCenter defaultCenter] addObserver:sharedInstance selector:@selector(onMediaUploadProgress:) name:kMXMediaUploadProgressNotification object:nil];
// Add observer to handle logout
@ -59,14 +65,15 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
// Add observer on the Extension host
[[NSNotificationCenter defaultCenter] addObserver:sharedInstance selector:@selector(checkUserAccount) name:NSExtensionHostWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:sharedInstance selector:@selector(suspendSession) name:NSExtensionHostDidEnterBackgroundNotification object:nil];
MXSDKOptions *sdkOptions = [MXSDKOptions sharedInstance];
// Apply the application group
sdkOptions.applicationGroupIdentifier = @"group.im.vector";
// Disable identicon use
sdkOptions.disableIdenticonUseForUserAvatar = YES;
// Enable e2e encryption for newly created MXSession
sdkOptions.enableCryptoWhenStartingMXSession = YES;
});
return sharedInstance;
}
@ -85,48 +92,31 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
if (!firstAccount || ![self.userAccount.mxCredentials.accessToken isEqualToString:firstAccount.mxCredentials.accessToken])
{
// Remove this account
[self.userAccount closeSession:YES];
self.userAccount = nil;
_mxSession = nil;
// Post notification
[[NSNotificationCenter defaultCenter] postNotificationName:kShareExtensionManagerDidChangeMXSessionNotification object:_mxSession userInfo:nil];
}
}
if (!self.userAccount)
{
// We consider the first enabled account.
// TODO: Handle multiple accounts
self.userAccount = [MXKAccountManager sharedManager].activeAccounts.firstObject;
}
// Reset the file store to reload the room data.
if (_fileStore)
{
[_fileStore close];
_fileStore = nil;
}
if (self.userAccount)
{
// Resume the matrix session
[self.userAccount resume];
_fileStore = [[MXFileStore alloc] initWithCredentials:self.userAccount.mxCredentials];
}
else
{
// Prepare a new session if a new account is available.
[self prepareSession];
}
}
- (void)prepareSession
{
// We consider the first enabled account.
// TODO: Handle multiple accounts
self.userAccount = [MXKAccountManager sharedManager].activeAccounts.firstObject;
if (self.userAccount)
{
NSLog(@"[ShareExtensionManager] openSession for %@ account", self.userAccount.mxCredentials.userId);
// Use MXFileStore as MXStore to permanently store events.
[self.userAccount openSessionWithStore:[[MXFileStore alloc] init]];
_mxSession = self.userAccount.mxSession;
// Post notification
[[NSNotificationCenter defaultCenter] postNotificationName:kShareExtensionManagerDidChangeMXSessionNotification object:_mxSession userInfo:nil];
}
}
- (void)suspendSession
{
[self.userAccount pauseInBackgroundTask];
// Post notification
[[NSNotificationCenter defaultCenter] postNotificationName:kShareExtensionManagerDidUpdateAccountDataNotification object:self.userAccount userInfo:nil];
}
#pragma mark - Public
@ -135,11 +125,11 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
{
_shareExtensionContext = shareExtensionContext;
// Prepare or resume the matrix session.
// Check the current matrix user.
[self checkUserAccount];
}
- (void)sendContentToRoom:(MXRoom *)room failureBlock:(void(^)())failureBlock
- (void)sendContentToRoom:(MXRoom *)room failureBlock:(void(^)(NSError *error))failureBlock
{
NSString *UTTypeText = (__bridge NSString *)kUTTypeText;
NSString *UTTypeURL = (__bridge NSString *)kUTTypeURL;
@ -150,6 +140,8 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
__weak typeof(self) weakSelf = self;
[self.pendingImages removeAllObjects];
for (NSExtensionItem *item in self.shareExtensionContext.inputItems)
{
for (NSItemProvider *itemProvider in item.attachments)
@ -207,30 +199,24 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
}
else if ([itemProvider hasItemConformingToTypeIdentifier:UTTypeImage])
{
[itemProvider loadItemForTypeIdentifier:UTTypeImage options:nil completionHandler:^(NSData *imageData, NSError * _Null_unspecified error) {
// Switch back on the main thread to handle correctly the UI change
dispatch_async(dispatch_get_main_queue(), ^{
if (weakSelf)
{
typeof(self) self = weakSelf;
UIImage *image = [[UIImage alloc] initWithData:imageData];
UIAlertController *compressionPrompt = [self compressionPromptForImage:image shareBlock:^{
[self sendImage:image withProvider:itemProvider toRoom:room extensionItem:item failureBlock:failureBlock];
}];
if (compressionPrompt)
{
[self.delegate shareExtensionManager:self showImageCompressionPrompt:compressionPrompt];
}
}
itemProvider.isLoaded = NO;
[itemProvider loadItemForTypeIdentifier:UTTypeImage options:nil completionHandler:^(NSData *imageData, NSError * _Null_unspecified error)
{
if (weakSelf)
{
itemProvider.isLoaded = YES;
[self.pendingImages addObject:imageData];
});
if ([self areAttachmentsFullyLoaded])
{
UIImage *firstImage = [UIImage imageWithData:self.pendingImages.firstObject];
UIAlertController *compressionPrompt = [self compressionPromptForImage:firstImage shareBlock:^{
[self sendImages:self.pendingImages withProviders:item.attachments toRoom:room extensionItem:item failureBlock:failureBlock];
}];
[self.delegate shareExtensionManager:self showImageCompressionPrompt:compressionPrompt];
}
}
}];
}
else if ([itemProvider hasItemConformingToTypeIdentifier:UTTypeVideo])
@ -288,8 +274,6 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
- (void)terminateExtensionCanceled:(BOOL)canceled
{
[self suspendSession];
if (canceled)
{
[self.shareExtensionContext cancelRequestWithError:[NSError errorWithDomain:@"MXUserCancelErrorDomain" code:4201 userInfo:nil]];
@ -303,12 +287,24 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
self.primaryViewController = nil;
}
- (void)roomFromRoomSummary:(MXRoomSummary *)roomSummary store:(MXFileStore *)fileStore session:(MXSession *)session
{
[fileStore asyncAccountDataOfRoom:roomSummary.roomId success:^(MXRoomAccountData * _Nonnull accountData) {
[fileStore asyncStateEventsOfRoom:roomSummary.roomId success:^(NSArray<MXEvent *> * _Nonnull roomStateEvents) {
MXRoom *room = [[MXRoom alloc] initWithRoomId:roomSummary.roomId andMatrixSession:session andStateEvents:roomStateEvents andAccountData:accountData];
} failure:^(NSError * _Nonnull error) {
//sjh
}];
} failure:^(NSError * _Nonnull error) {
//shj
}];
}
#pragma mark - Private
- (void)completeRequestReturningItems:(nullable NSArray *)items completionHandler:(void(^ __nullable)(BOOL expired))completionHandler;
{
[self suspendSession];
[self.shareExtensionContext completeRequestReturningItems:items completionHandler:completionHandler];
[self.primaryViewController destroy];
@ -471,19 +467,44 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
}
}
- (BOOL)areAttachmentsFullyLoaded
{
for (NSExtensionItem *item in self.shareExtensionContext.inputItems)
{
for (NSItemProvider *itemProvider in item.attachments)
{
if (itemProvider.isLoaded == NO)
{
return NO;
}
}
}
return YES;
}
#pragma mark - Notifications
- (void)onMediaUploadProgress:(NSNotification *)notification
{
self.imageUploadProgresses[notification.object] = (NSNumber *)notification.userInfo[kMXMediaLoaderProgressValueKey];
if ([self.delegate respondsToSelector:@selector(shareExtensionManager:mediaUploadProgress:)])
{
[self.delegate shareExtensionManager:self mediaUploadProgress:((NSNumber *)notification.userInfo[kMXMediaLoaderProgressValueKey]).floatValue];
const NSInteger totalImagesCount = self.pendingImages.count;
CGFloat totalProgress = 0.0;
for (NSNumber *progress in self.imageUploadProgresses.allValues)
{
totalProgress += progress.floatValue/totalImagesCount;
}
[self.delegate shareExtensionManager:self mediaUploadProgress:totalProgress];
}
}
#pragma mark - Sharing
- (void)sendText:(NSString *)text toRoom:(MXRoom *)room extensionItem:(NSExtensionItem *)extensionItem failureBlock:(void(^)())failureBlock
- (void)sendText:(NSString *)text toRoom:(MXRoom *)room extensionItem:(NSExtensionItem *)extensionItem failureBlock:(void(^)(NSError *error))failureBlock
{
[self didStartSendingToRoom:room];
if (!text)
@ -491,13 +512,12 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
NSLog(@"[ShareExtensionManager] loadItemForTypeIdentifier: failed.");
if (failureBlock)
{
failureBlock();
failureBlock(nil);
}
return;
}
__weak typeof(self) weakSelf = self;
[room sendTextMessage:text success:^(NSString *eventId) {
if (weakSelf)
{
@ -508,12 +528,12 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
NSLog(@"[ShareExtensionManager] sendTextMessage failed.");
if (failureBlock)
{
failureBlock();
failureBlock(error);
}
}];
}
- (void)sendFileWithUrl:(NSURL *)fileUrl toRoom:(MXRoom *)room extensionItem:(NSExtensionItem *)extensionItem failureBlock:(void(^)())failureBlock
- (void)sendFileWithUrl:(NSURL *)fileUrl toRoom:(MXRoom *)room extensionItem:(NSExtensionItem *)extensionItem failureBlock:(void(^)(NSError *error))failureBlock
{
[self didStartSendingToRoom:room];
if (!fileUrl)
@ -521,7 +541,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
NSLog(@"[ShareExtensionManager] loadItemForTypeIdentifier: failed.");
if (failureBlock)
{
failureBlock();
failureBlock(nil);
}
return;
}
@ -543,85 +563,108 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
NSLog(@"[ShareExtensionManager] sendFile failed.");
if (failureBlock)
{
failureBlock();
failureBlock(error);
}
} keepActualFilename:YES];
}
- (void)sendImage:(UIImage *)image withProvider:(NSItemProvider*)itemProvider toRoom:(MXRoom *)room extensionItem:(NSExtensionItem *)extensionItem failureBlock:(void(^)())failureBlock
- (void)sendImages:(NSMutableArray *)imageDatas withProviders:(NSArray*)itemProviders toRoom:(MXRoom *)room extensionItem:(NSExtensionItem *)extensionItem failureBlock:(void(^)(NSError *error))failureBlock
{
[self didStartSendingToRoom:room];
if (!image)
for (NSInteger index = 0; index < imageDatas.count; index++)
{
NSLog(@"[ShareExtensionManager] loadItemForTypeIdentifier: failed.");
if (failureBlock)
NSItemProvider *itemProvider = itemProviders[index];
NSData *imageData = imageDatas[index];
UIImage *image = [UIImage imageWithData:imageData];
if (!imageData)
{
failureBlock();
NSLog(@"[ShareExtensionManager] loadItemForTypeIdentifier: failed.");
if (failureBlock)
{
failureBlock(nil);
failureBlock = nil;
}
return;
}
return;
}
// Prepare the image
NSData *imageData;
if (self.imageCompressionMode == ImageCompressionModeSmall)
{
image = [MXKTools reduceImage:image toFitInSize:CGSizeMake(MXKTOOLS_SMALL_IMAGE_SIZE, MXKTOOLS_SMALL_IMAGE_SIZE)];
}
else if (self.imageCompressionMode == ImageCompressionModeMedium)
{
image = [MXKTools reduceImage:image toFitInSize:CGSizeMake(MXKTOOLS_MEDIUM_IMAGE_SIZE, MXKTOOLS_MEDIUM_IMAGE_SIZE)];
}
else if (self.imageCompressionMode == ImageCompressionModeLarge)
{
image = [MXKTools reduceImage:image toFitInSize:CGSizeMake(self.actualLargeSize, self.actualLargeSize)];
}
// Make sure the uploaded image orientation is up
image = [MXKTools forceImageOrientationUp:image];
NSString *mimeType;
if ([itemProvider hasItemConformingToTypeIdentifier:(__bridge NSString *)kUTTypePNG])
{
mimeType = @"image/png";
imageData = UIImagePNGRepresentation(image);
}
else
{
// Use jpeg format by default.
mimeType = @"image/jpeg";
imageData = UIImageJPEGRepresentation(image, 0.9);
}
UIImage *thumbnail = nil;
// Thumbnail is useful only in case of encrypted room
if (room.state.isEncrypted)
{
thumbnail = [MXKTools reduceImage:image toFitInSize:CGSizeMake(800, 600)];
if (thumbnail == image)
// Prepare the image
NSData *convertedImageData;
if (self.imageCompressionMode == ImageCompressionModeSmall)
{
thumbnail = nil;
image = [MXKTools reduceImage:image toFitInSize:CGSizeMake(MXKTOOLS_SMALL_IMAGE_SIZE, MXKTOOLS_SMALL_IMAGE_SIZE)];
}
else if (self.imageCompressionMode == ImageCompressionModeMedium)
{
image = [MXKTools reduceImage:image toFitInSize:CGSizeMake(MXKTOOLS_MEDIUM_IMAGE_SIZE, MXKTOOLS_MEDIUM_IMAGE_SIZE)];
}
else if (self.imageCompressionMode == ImageCompressionModeLarge)
{
image = [MXKTools reduceImage:image toFitInSize:CGSizeMake(self.actualLargeSize, self.actualLargeSize)];
}
// Make sure the uploaded image orientation is up
image = [MXKTools forceImageOrientationUp:image];
NSString *mimeType;
if ([itemProvider hasItemConformingToTypeIdentifier:(__bridge NSString *)kUTTypePNG])
{
mimeType = @"image/png";
convertedImageData = UIImagePNGRepresentation(image);
}
else
{
// Use jpeg format by default.
mimeType = @"image/jpeg";
convertedImageData = UIImageJPEGRepresentation(image, 0.9);
}
UIImage *thumbnail = nil;
// Thumbnail is useful only in case of encrypted room
if (room.state.isEncrypted)
{
thumbnail = [MXKTools reduceImage:image toFitInSize:CGSizeMake(800, 600)];
if (thumbnail == image)
{
thumbnail = nil;
}
}
__weak typeof(self) weakSelf = self;
[room sendImage:convertedImageData withImageSize:image.size mimeType:mimeType andThumbnail:thumbnail localEcho:nil success:^(NSString *eventId) {
if (weakSelf)
{
typeof(self) self = weakSelf;
[imageDatas removeObject:imageData];
if (!imageDatas.count)
{
[self.shareExtensionContext completeRequestReturningItems:@[extensionItem] completionHandler:nil];
}
}
} failure:^(NSError *error) {
NSLog(@"[ShareExtensionManager] sendImage failed.");
[imageDatas removeObject:imageData];
if (!imageDatas.count)
{
if (failureBlock)
{
failureBlock(error);
}
}
}];
}
__weak typeof(self) weakSelf = self;
[room sendImage:imageData withImageSize:image.size mimeType:mimeType andThumbnail:thumbnail localEcho:nil success:^(NSString *eventId) {
if (weakSelf)
{
typeof(self) self = weakSelf;
[self completeRequestReturningItems:@[extensionItem] completionHandler:nil];
}
} failure:^(NSError *error) {
NSLog(@"[ShareExtensionManager] sendImage failed.");
if (failureBlock)
{
failureBlock();
}
}];
}
- (void)sendVideo:(NSURL *)videoLocalUrl toRoom:(MXRoom *)room extensionItem:(NSExtensionItem *)extensionItem failureBlock:(void(^)())failureBlock
- (void)sendVideo:(NSURL *)videoLocalUrl toRoom:(MXRoom *)room extensionItem:(NSExtensionItem *)extensionItem failureBlock:(void(^)(NSError *error))failureBlock
{
[self didStartSendingToRoom:room];
if (!videoLocalUrl)
@ -629,7 +672,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
NSLog(@"[ShareExtensionManager] loadItemForTypeIdentifier: failed.");
if (failureBlock)
{
failureBlock();
failureBlock(nil);
}
return;
}
@ -656,10 +699,27 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
NSLog(@"[ShareExtensionManager] sendVideo failed.");
if (failureBlock)
{
failureBlock();
failureBlock(error);
}
}];
}
@end
@implementation NSItemProvider (ShareExtensionManager)
- (void)setIsLoaded:(BOOL)isLoaded
{
NSNumber *number = [NSNumber numberWithBool:isLoaded];
objc_setAssociatedObject(self, @selector(isLoaded), number, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)isLoaded
{
NSNumber *number = objc_getAssociatedObject(self, @selector(isLoaded));
return number.boolValue;
}
@end

View file

@ -1,144 +0,0 @@
/*
Copyright 2017 Aram Sargsyan
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 "ShareRecentsDataSource.h"
#import "RoomTableViewCell.h"
#import "RecentCellData.h"
@interface ShareRecentsDataSource ()
@property (nonatomic, readwrite) ShareRecentsDataSourceMode dataSourceMode;
@property NSMutableArray *recentRooms;
@property NSMutableArray *recentPeople;
@end
@implementation ShareRecentsDataSource
- (instancetype)initWithMatrixSession:(MXSession *)mxSession dataSourceMode:(ShareRecentsDataSourceMode)dataSourceMode
{
self = [super initWithMatrixSession:mxSession];
if (self)
{
self.dataSourceMode = dataSourceMode;
_recentRooms = [NSMutableArray array];
_recentPeople = [NSMutableArray array];
}
return self;
}
#pragma mark - Private
- (void)updateArrays
{
[self.recentPeople removeAllObjects];
[self.recentRooms removeAllObjects];
MXKSessionRecentsDataSource *recentsDataSource = displayedRecentsDataSourceArray.firstObject;
NSInteger count = recentsDataSource.numberOfCells;
for (int index = 0; index < count; index++)
{
id<MXKRecentCellDataStoring> cellData = [recentsDataSource cellDataAtIndex:index];
MXRoom* room = cellData.roomSummary.room;
if (self.dataSourceMode == RecentsDataSourceModePeople)
{
if (room.isDirect && room.state.membership == MXMembershipJoin)
{
[self.recentPeople addObject:cellData];
}
}
else if (self.dataSourceMode == RecentsDataSourceModeRooms)
{
if (!room.isDirect && room.state.membership == MXMembershipJoin)
{
[self.recentRooms addObject:cellData];
}
}
}
}
#pragma mark - MXKRecentsDataSource
- (Class)cellDataClassForCellIdentifier:(NSString *)identifier
{
return RecentCellData.class;
}
- (id<MXKRecentCellDataStoring>)cellDataAtIndexPath:(NSIndexPath *)indexPath
{
id<MXKRecentCellDataStoring> cellData = nil;
if (self.dataSourceMode == RecentsDataSourceModePeople)
{
cellData = self.recentPeople[indexPath.row];
}
else
{
cellData = self.recentRooms[indexPath.row];
}
return cellData;
}
- (void)dataSource:(MXKDataSource*)dataSource didCellChange:(id)changes
{
[super dataSource:dataSource didCellChange:changes];
[self updateArrays];
[self.delegate dataSource:self didCellChange:changes];
}
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.dataSourceMode == RecentsDataSourceModePeople)
{
return self.recentPeople.count;
}
else
{
return self.recentRooms.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
RoomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[RoomTableViewCell defaultReuseIdentifier]];
MXRoom *room = [self cellDataAtIndexPath:indexPath].roomSummary.room;
[cell render:room];
if (!room.summary.displayname.length && !cell.titleLabel.text.length)
{
cell.titleLabel.text = NSLocalizedStringFromTable(@"room_displayname_no_title", @"Vector", nil);
}
return cell;
}
@end

View file

@ -30,7 +30,7 @@
[super viewDidLoad];
self.titleLabel.textColor = kRiotSecondaryTextColor;
self.titleLabel.text = NSLocalizedStringFromTable(@"auth_share_extension_prompt", @"Vector", nil);
self.titleLabel.text = NSLocalizedStringFromTable(@"share_extension_auth_prompt", @"Vector", nil);
}
- (void)didReceiveMemoryWarning

View file

@ -16,7 +16,7 @@
#import <UIKit/UIKit.h>
#import "MXRoom+Riot.h"
#import "ShareRecentsDataSource.h"
#import "ShareDataSource.h"
@interface RoomsListViewController : MXKRecentListViewController

View file

@ -15,7 +15,7 @@
*/
#import "RoomsListViewController.h"
#import "RoomTableViewCell.h"
#import "RecentRoomTableViewCell.h"
#import "NSBundle+MatrixKit.h"
#import "ShareExtensionManager.h"
#import "RecentCellData.h"
@ -23,8 +23,6 @@
#import "MXKPieChartView.h"
#import "MXKPieChartHUD.h"
@interface RoomsListViewController () <ShareExtensionManagerDelegate>
@property (nonatomic) MXKPieChartHUD *hudView;
@ -35,7 +33,6 @@
@end
@implementation RoomsListViewController
#pragma mark - Class methods
@ -73,7 +70,7 @@
{
[super viewDidLoad];
[self.recentsTableView registerNib:[RoomTableViewCell nib] forCellReuseIdentifier:[RoomTableViewCell defaultReuseIdentifier]];
[self.recentsTableView registerNib:[RecentRoomTableViewCell nib] forCellReuseIdentifier:[RecentRoomTableViewCell defaultReuseIdentifier]];
[self configureSearchBar];
}
@ -130,35 +127,78 @@
- (void)showShareAlertForRoomPath:(NSIndexPath *)indexPath
{
// @TODO: the room should be instanciated here (only the room summary should be available from dataSource).
NSString *receipantName = [self.dataSource getRoomAtIndexPath:indexPath].summary.displayname;
if (!receipantName.length)
MXKRecentCellData *recentCellData = [self.dataSource cellDataAtIndexPath:indexPath];
NSString *roomName = recentCellData.roomSummary.displayname;
if (!roomName.length)
{
receipantName = NSLocalizedStringFromTable(@"room_displayname_no_title", @"Vector", nil);
roomName = NSLocalizedStringFromTable(@"room_displayname_no_title", @"Vector", nil);
}
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:NSLocalizedStringFromTable(@"send_to", @"Vector", nil), receipantName] message:nil preferredStyle:UIAlertControllerStyleAlert];
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:NSLocalizedStringFromTable(@"send_to", @"Vector", nil), roomName] message:nil preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] style:UIAlertActionStyleCancel handler:nil];
[alertController addAction:cancelAction];
UIAlertAction *sendAction = [UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"send"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
MXRoom *selectedRoom = [self.dataSource getRoomAtIndexPath:indexPath];
[ShareExtensionManager sharedManager].delegate = self;
[[ShareExtensionManager sharedManager] sendContentToRoom:selectedRoom failureBlock:^{
[self showFailureAlert];
// The selected room is instanciated here.
[[ShareExtensionManager sharedManager].fileStore asyncAccountDataOfRoom:recentCellData.roomSummary.roomId success:^(MXRoomAccountData * _Nonnull accountData) {
[[ShareExtensionManager sharedManager].fileStore asyncStateEventsOfRoom:recentCellData.roomSummary.roomId success:^(NSArray<MXEvent *> * _Nonnull roomStateEvents) {
MXSession *session = [[MXSession alloc] initWithMatrixRestClient:[[MXRestClient alloc] initWithCredentials:[ShareExtensionManager sharedManager].userAccount.mxCredentials andOnUnrecognizedCertificateBlock:nil]];
// To handle correctly the crypto, we have to set a store (use a fake store)
__weak MXSession *weakSession = session;
[session setStore:[[MXNoStore alloc] init] success:^{
if (weakSession)
{
__strong MXSession *session = weakSession;
MXRoom *selectedRoom = [[MXRoom alloc] initWithRoomId:recentCellData.roomSummary.roomId andMatrixSession:session andStateEvents:roomStateEvents andAccountData:accountData];
[ShareExtensionManager sharedManager].delegate = self;
[[ShareExtensionManager sharedManager] sendContentToRoom:selectedRoom failureBlock:^(NSError* error) {
NSString *title;
if ([error.domain isEqualToString:MXEncryptingErrorDomain])
{
title = NSLocalizedStringFromTable(@"share_extension_failed_to_encrypt", @"Vector", nil);
}
[self showFailureAlert:title];
}];
}
} failure:^(NSError *error) {
NSLog(@"[RoomsListViewController] failed to prepare matrix session]");
}];
} failure:^(NSError * _Nonnull error) {
NSLog(@"[RoomsListViewController] failed to get state events");
}];
} failure:^(NSError * _Nonnull error) {
NSLog(@"[RoomsListViewController] failed to get account data");
}];
}];
[alertController addAction:sendAction];
[self presentViewController:alertController animated:YES completion:nil];
}
- (void)showFailureAlert
- (void)showFailureAlert:(NSString *)title
{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"room_event_failed_to_send", @"Vector", nil) message:nil preferredStyle:UIAlertControllerStyleAlert];
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title.length ? title : NSLocalizedStringFromTable(@"room_event_failed_to_send", @"Vector", nil) message:nil preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
if (self.failureBlock)
{
@ -173,7 +213,7 @@
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [RoomTableViewCell cellHeight];
return [RecentRoomTableViewCell cellHeight];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
@ -189,7 +229,7 @@
{
if ([cellData isKindOfClass:[RecentCellData class]])
{
return [RoomTableViewCell class];
return [RecentRoomTableViewCell class];
}
return nil;
}
@ -198,13 +238,23 @@
{
if ([cellData isKindOfClass:[MXKRecentCellData class]])
{
return [RoomTableViewCell defaultReuseIdentifier];
return [RecentRoomTableViewCell defaultReuseIdentifier];
}
return nil;
}
#pragma mark - UISearchBarDelegate
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
NSArray *patterns = nil;
if (searchText.length)
{
patterns = @[searchText];
}
[self.dataSource searchWithPatterns:patterns];
}
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
{
if (searchBar == _tableSearchBar)
@ -229,6 +279,7 @@
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
[self.recentsSearchBar setShowsCancelButton:NO animated:NO];
[self.dataSource searchWithPatterns:nil];
}
#pragma mark - UIScrollViewDelegate
@ -241,7 +292,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];
@ -264,9 +315,12 @@
- (void)shareExtensionManager:(ShareExtensionManager *)extensionManager didStartSendingContentToRoom:(MXRoom *)room
{
self.parentViewController.view.userInteractionEnabled = NO;
self.hudView = [MXKPieChartHUD showLoadingHudOnView:self.view WithMessage:NSLocalizedStringFromTable(@"sending", @"Vector", nil)];
[self.hudView setProgress:0.0];
if (!self.hudView)
{
self.parentViewController.view.userInteractionEnabled = NO;
self.hudView = [MXKPieChartHUD showLoadingHudOnView:self.view WithMessage:NSLocalizedStringFromTable(@"sending", @"Vector", nil)];
[self.hudView setProgress:0.0];
}
}
- (void)shareExtensionManager:(ShareExtensionManager *)extensionManager mediaUploadProgress:(CGFloat)progress

View file

@ -18,7 +18,7 @@
#import "SegmentedViewController.h"
#import "RoomsListViewController.h"
#import "FallbackViewController.h"
#import "ShareRecentsDataSource.h"
#import "ShareDataSource.h"
#import "ShareExtensionManager.h"
@ -30,7 +30,7 @@
@property (nonatomic) SegmentedViewController *segmentedViewController;
@property (nonatomic) id shareExtensionManagerDidChangeMXSessionObserver;
@property (nonatomic) id shareExtensionManagerDidUpdateAccountDataObserver;
@end
@ -44,10 +44,10 @@
{
[super viewDidLoad];
self.shareExtensionManagerDidChangeMXSessionObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kShareExtensionManagerDidChangeMXSessionNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
self.shareExtensionManagerDidUpdateAccountDataObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kShareExtensionManagerDidUpdateAccountDataNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
[self configureViews];
}];
[self configureViews];
@ -57,10 +57,10 @@
{
[super destroy];
if (self.shareExtensionManagerDidChangeMXSessionObserver)
if (self.shareExtensionManagerDidUpdateAccountDataObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:self.shareExtensionManagerDidChangeMXSessionObserver];
self.shareExtensionManagerDidChangeMXSessionObserver = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self.shareExtensionManagerDidUpdateAccountDataObserver];
self.shareExtensionManagerDidUpdateAccountDataObserver = nil;
}
[self resetContentView];
@ -94,7 +94,7 @@
[self resetContentView];
if ([ShareExtensionManager sharedManager].mxSession)
if ([ShareExtensionManager sharedManager].userAccount)
{
self.tittleLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"send_to", @"Vector", nil), @""];
[self configureSegmentedViewController];
@ -120,12 +120,12 @@
}];
};
ShareRecentsDataSource *roomsDataSource = [[ShareRecentsDataSource alloc] initWithMatrixSession:[ShareExtensionManager sharedManager].mxSession dataSourceMode:RecentsDataSourceModeRooms];
ShareDataSource *roomsDataSource = [[ShareDataSource alloc] initWithMode:DataSourceModeRooms];
RoomsListViewController *roomsViewController = [RoomsListViewController recentListViewController];
roomsViewController.failureBlock = failureBlock;
[roomsViewController displayList:roomsDataSource];
ShareRecentsDataSource *peopleDataSource = [[ShareRecentsDataSource alloc] initWithMatrixSession:[ShareExtensionManager sharedManager].mxSession dataSourceMode:RecentsDataSourceModePeople];
ShareDataSource *peopleDataSource = [[ShareDataSource alloc] initWithMode:DataSourceModePeople];
RoomsListViewController *peopleViewController = [RoomsListViewController recentListViewController];
peopleViewController.failureBlock = failureBlock;
[peopleViewController displayList:peopleDataSource];

View file

@ -16,14 +16,8 @@
#import <MatrixKit/MatrixKit.h>
typedef NS_ENUM(NSInteger, ShareRecentsDataSourceMode)
{
RecentsDataSourceModePeople,
RecentsDataSourceModeRooms
};
@interface RecentRoomTableViewCell : MXKRecentTableViewCell
@interface ShareRecentsDataSource : MXKRecentsDataSource
- (instancetype)initWithMatrixSession:(MXSession *)mxSession dataSourceMode:(ShareRecentsDataSourceMode)dataSourceMode;
+ (CGFloat)cellHeight;
@end

View file

@ -0,0 +1,83 @@
/*
Copyright 2017 Aram Sargsyan
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 "RecentRoomTableViewCell.h"
#import "MXRoomSummary+Riot.h"
@interface RecentRoomTableViewCell ()
@property (weak, nonatomic) IBOutlet MXKImageView *avatarImageView;
@property (weak, nonatomic) IBOutlet UIView *directRoomBorderView;
@property (weak, nonatomic) IBOutlet UILabel *roomTitleLabel;
@property (weak, nonatomic) IBOutlet UIImageView *encryptedRoomIcon;
@end
@implementation RecentRoomTableViewCell
#pragma mark - MXKRecentTableViewCell
+ (UINib *)nib
{
// Check whether a nib file is available
NSBundle *mainBundle = [NSBundle bundleForClass:self.class];
NSString *path = [mainBundle pathForResource:NSStringFromClass([self class]) ofType:@"nib"];
if (path)
{
return [UINib nibWithNibName:NSStringFromClass([self class]) bundle:mainBundle];
}
return nil;
}
- (void)layoutSubviews
{
[super layoutSubviews];
// Round room avatars
[self.avatarImageView.layer setCornerRadius:self.avatarImageView.frame.size.width / 2];
self.avatarImageView.clipsToBounds = YES;
}
- (void)render:(MXKCellData *)cellData
{
// Sanity check: accept only object of MXKRecentCellData classes or sub-classes
NSParameterAssert([cellData isKindOfClass:[MXKRecentCellData class]]);
roomCellData = (id<MXKRecentCellDataStoring>)cellData;
if (roomCellData)
{
[roomCellData.roomSummary setRoomAvatarImageIn:self.avatarImageView];
self.roomTitleLabel.text = roomCellData.roomSummary.displayname;
if (!self.roomTitleLabel.text.length)
{
self.roomTitleLabel.text = NSLocalizedStringFromTable(@"room_displayname_no_title", @"Vector", nil);
}
self.directRoomBorderView.hidden = !roomCellData.roomSummary.isDirect;
self.encryptedRoomIcon.hidden = !roomCellData.roomSummary.isEncrypted;
}
}
+ (CGFloat)cellHeight
{
return 74;
}
@end

View file

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13196" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13173"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" id="3kk-d5-Wja" customClass="RecentRoomTableViewCell">
<rect key="frame" x="0.0" y="0.0" width="600" height="74"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="3kk-d5-Wja" id="b83-aU-AJ3">
<rect key="frame" x="0.0" y="0.0" width="600" height="73.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="HJd-Am-cCx" customClass="MXKImageView">
<rect key="frame" x="14" y="16" width="42" height="42"/>
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="AvatarImageView"/>
<constraints>
<constraint firstAttribute="width" secondItem="HJd-Am-cCx" secondAttribute="height" multiplier="1:1" id="CVW-Lp-6O4"/>
<constraint firstAttribute="width" constant="42" id="Xp0-5S-1wI"/>
</constraints>
</view>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Fbf-fG-GSb">
<rect key="frame" x="14" y="16" width="42" height="42"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" secondItem="Fbf-fG-GSb" secondAttribute="height" multiplier="1:1" id="1gX-EI-Ffa"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vBS-iO-8H6">
<rect key="frame" x="70" y="27" width="34" height="20"/>
<accessibility key="accessibilityConfiguration" identifier="TitleLabel"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="H21-1K-fGz"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="e2e_verified.png" translatesAutoresizingMaskIntoConstraints="NO" id="hqD-YY-LWz">
<rect key="frame" x="50" y="42" width="11" height="13"/>
<accessibility key="accessibilityConfiguration" identifier="EncryptedRoomIcon"/>
<constraints>
<constraint firstAttribute="height" constant="13" id="8jQ-rK-hf2"/>
<constraint firstAttribute="width" constant="11" id="Mai-xD-TqV"/>
</constraints>
</imageView>
</subviews>
<constraints>
<constraint firstItem="HJd-Am-cCx" firstAttribute="leading" secondItem="b83-aU-AJ3" secondAttribute="leadingMargin" constant="6" id="4df-2f-865"/>
<constraint firstItem="hqD-YY-LWz" firstAttribute="top" secondItem="b83-aU-AJ3" secondAttribute="topMargin" constant="34" id="Cdm-v3-js8"/>
<constraint firstItem="Fbf-fG-GSb" firstAttribute="width" secondItem="HJd-Am-cCx" secondAttribute="width" id="RBA-bw-MFn"/>
<constraint firstItem="hqD-YY-LWz" firstAttribute="leading" secondItem="b83-aU-AJ3" secondAttribute="leadingMargin" constant="42" id="UIE-13-LGw"/>
<constraint firstItem="Fbf-fG-GSb" firstAttribute="centerY" secondItem="HJd-Am-cCx" secondAttribute="centerY" id="Xxe-pe-r6t"/>
<constraint firstItem="HJd-Am-cCx" firstAttribute="centerY" secondItem="b83-aU-AJ3" secondAttribute="centerY" id="flh-LO-k3n"/>
<constraint firstItem="Fbf-fG-GSb" firstAttribute="centerX" secondItem="HJd-Am-cCx" secondAttribute="centerX" id="oKN-d4-vd0"/>
<constraint firstItem="vBS-iO-8H6" firstAttribute="centerY" secondItem="b83-aU-AJ3" secondAttribute="centerY" id="ocY-mt-0n0"/>
<constraint firstAttribute="trailingMargin" relation="greaterThanOrEqual" secondItem="vBS-iO-8H6" secondAttribute="trailing" constant="15" id="qqy-zs-Ccy"/>
<constraint firstItem="vBS-iO-8H6" firstAttribute="leading" secondItem="HJd-Am-cCx" secondAttribute="trailing" constant="14" id="quv-47-R9n"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="RoomTableViewCell"/>
<connections>
<outlet property="avatarImageView" destination="HJd-Am-cCx" id="Mv2-Kb-aG4"/>
<outlet property="directRoomBorderView" destination="Fbf-fG-GSb" id="aid-IX-joJ"/>
<outlet property="encryptedRoomIcon" destination="hqD-YY-LWz" id="Ikp-Sf-Vlc"/>
<outlet property="roomTitleLabel" destination="vBS-iO-8H6" id="0J9-p3-Lzx"/>
</connections>
<point key="canvasLocation" x="-53" y="-43"/>
</tableViewCell>
</objects>
<resources>
<image name="e2e_verified.png" width="10" height="12"/>
</resources>
</document>