mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-05-20 22:52:12 +02:00
Finish v0.6.16
This commit is contained in:
+16
@@ -1,3 +1,19 @@
|
||||
Changes in 0.6.16 (2018-05-23)
|
||||
===============================================
|
||||
|
||||
Improvements:
|
||||
* Upgrade MatrixKit version (v0.7.12).
|
||||
* Display quick replies in timeline (#1858).
|
||||
* Beginning of "Send sticker" support (#1860).
|
||||
* Use existing message.mp3 for notification sounds, thanks to @pixlwave (PR #1835).
|
||||
* GDPR: Display the consent tool in case of M_CONSENT_NOT_GIVEN error (#1871).
|
||||
|
||||
Bug fix:
|
||||
* Fix the display of side borders of HTML blockquotes (#1857).
|
||||
* Moved UI update to main queue, thanks to @Taiwo (PR #1854).
|
||||
* Timestamps say 'Yesterday' when it is today (#1274), thanks to @pixlwave (PR #1865).
|
||||
* RoomVC: messages with link blink forever #1869
|
||||
|
||||
Changes in 0.6.15 (2018-04-23)
|
||||
===============================================
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ source 'https://github.com/CocoaPods/Specs.git'
|
||||
|
||||
# Different flavours of pods to MatrixKit
|
||||
# The current MatrixKit pod version
|
||||
$matrixKitVersion = '0.7.11'
|
||||
$matrixKitVersion = '0.7.12'
|
||||
|
||||
# The develop branch version
|
||||
#$matrixKitVersion = 'develop'
|
||||
|
||||
+45
-29
@@ -1,18 +1,18 @@
|
||||
PODS:
|
||||
- AFNetworking (3.2.0):
|
||||
- AFNetworking/NSURLSession (= 3.2.0)
|
||||
- AFNetworking/Reachability (= 3.2.0)
|
||||
- AFNetworking/Security (= 3.2.0)
|
||||
- AFNetworking/Serialization (= 3.2.0)
|
||||
- AFNetworking/UIKit (= 3.2.0)
|
||||
- AFNetworking/NSURLSession (3.2.0):
|
||||
- AFNetworking (3.2.1):
|
||||
- AFNetworking/NSURLSession (= 3.2.1)
|
||||
- AFNetworking/Reachability (= 3.2.1)
|
||||
- AFNetworking/Security (= 3.2.1)
|
||||
- AFNetworking/Serialization (= 3.2.1)
|
||||
- AFNetworking/UIKit (= 3.2.1)
|
||||
- AFNetworking/NSURLSession (3.2.1):
|
||||
- AFNetworking/Reachability
|
||||
- AFNetworking/Security
|
||||
- AFNetworking/Serialization
|
||||
- AFNetworking/Reachability (3.2.0)
|
||||
- AFNetworking/Security (3.2.0)
|
||||
- AFNetworking/Serialization (3.2.0)
|
||||
- AFNetworking/UIKit (3.2.0):
|
||||
- AFNetworking/Reachability (3.2.1)
|
||||
- AFNetworking/Security (3.2.1)
|
||||
- AFNetworking/Serialization (3.2.1)
|
||||
- AFNetworking/UIKit (3.2.1):
|
||||
- AFNetworking/NSURLSession
|
||||
- cmark (0.24.1)
|
||||
- DTCoreText (1.6.21):
|
||||
@@ -43,34 +43,34 @@ PODS:
|
||||
- GZIP (1.2.1)
|
||||
- HPGrowingTextView (1.1)
|
||||
- libPhoneNumber-iOS (0.9.13)
|
||||
- MatrixKit (0.7.11):
|
||||
- MatrixKit (0.7.12):
|
||||
- cmark (~> 0.24.1)
|
||||
- DTCoreText (~> 1.6.21)
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixKit/Core (= 0.7.11)
|
||||
- MatrixSDK (= 0.10.9)
|
||||
- MatrixKit/AppExtension (0.7.11):
|
||||
- MatrixKit/Core (= 0.7.12)
|
||||
- MatrixSDK (= 0.10.10)
|
||||
- MatrixKit/AppExtension (0.7.12):
|
||||
- cmark (~> 0.24.1)
|
||||
- DTCoreText (~> 1.6.21)
|
||||
- DTCoreText/Extension
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixSDK (= 0.10.9)
|
||||
- MatrixKit/Core (0.7.11):
|
||||
- MatrixSDK (= 0.10.10)
|
||||
- MatrixKit/Core (0.7.12):
|
||||
- cmark (~> 0.24.1)
|
||||
- DTCoreText (~> 1.6.21)
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixSDK (= 0.10.9)
|
||||
- MatrixSDK (0.10.9):
|
||||
- MatrixSDK/Core (= 0.10.9)
|
||||
- MatrixSDK/Core (0.10.9):
|
||||
- MatrixSDK (= 0.10.10)
|
||||
- MatrixSDK (0.10.10):
|
||||
- MatrixSDK/Core (= 0.10.10)
|
||||
- MatrixSDK/Core (0.10.10):
|
||||
- AFNetworking (~> 3.2.0)
|
||||
- GZIP (~> 1.2.1)
|
||||
- OLMKit (~> 2.2.2)
|
||||
- Realm (~> 3.3.2)
|
||||
- MatrixSDK/JingleCallStack (0.10.9):
|
||||
- MatrixSDK/JingleCallStack (0.10.10):
|
||||
- MatrixSDK/Core
|
||||
- WebRTC (= 63.11.20455)
|
||||
- OLMKit (2.2.2):
|
||||
@@ -90,12 +90,28 @@ DEPENDENCIES:
|
||||
- cmark
|
||||
- DTCoreText
|
||||
- GBDeviceInfo (~> 5.1.0)
|
||||
- MatrixKit (= 0.7.11)
|
||||
- MatrixKit/AppExtension (= 0.7.11)
|
||||
- MatrixKit (= 0.7.12)
|
||||
- MatrixKit/AppExtension (= 0.7.12)
|
||||
- MatrixSDK/JingleCallStack
|
||||
- OLMKit
|
||||
- PiwikTracker (from `https://github.com/manuroe/matomo-sdk-ios.git`, branch `feature/CustomVariables`)
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/CocoaPods/Specs.git:
|
||||
- AFNetworking
|
||||
- cmark
|
||||
- DTCoreText
|
||||
- DTFoundation
|
||||
- GBDeviceInfo
|
||||
- GZIP
|
||||
- HPGrowingTextView
|
||||
- libPhoneNumber-iOS
|
||||
- MatrixKit
|
||||
- MatrixSDK
|
||||
- OLMKit
|
||||
- Realm
|
||||
- WebRTC
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
PiwikTracker:
|
||||
:branch: feature/CustomVariables
|
||||
@@ -107,7 +123,7 @@ CHECKOUT OPTIONS:
|
||||
:git: https://github.com/manuroe/matomo-sdk-ios.git
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
AFNetworking: 8ac6017b94ea105479f7776e5288e48ae9c59bb4
|
||||
AFNetworking: b6f891fdfaed196b46c7a83cf209e09697b94057
|
||||
cmark: ec0275215b504780287b6fca360224e384368af8
|
||||
DTCoreText: e5d688cffc9f6a61eddd1a4f94e2046851230de3
|
||||
DTFoundation: f03be9fd786f11e505bb8fc44e2a3732bf0917df
|
||||
@@ -115,13 +131,13 @@ SPEC CHECKSUMS:
|
||||
GZIP: 7ee835f989fb3c6ea79005fc90b8fa6af710a70d
|
||||
HPGrowingTextView: 88a716d97fb853bcb08a4a08e4727da17efc9b19
|
||||
libPhoneNumber-iOS: e444379ac18bbfbdefad571da735b2cd7e096caa
|
||||
MatrixKit: fa148ae8eef4e60e65d36af0af1be28fc8264935
|
||||
MatrixSDK: 5c3a0ddfa141775d88565e627f491a066dd1e87c
|
||||
MatrixKit: ac91b2667d1be1895994ee78654d6193a6644b09
|
||||
MatrixSDK: c74ed8b41e3e55b7510d7bddf612ed1985816e04
|
||||
OLMKit: b9d8c0ffee9ea8c45bc0aaa9afb47f93fba7efbd
|
||||
PiwikTracker: 42862c7b13028065c3dfd36b4dc38db8a5765acf
|
||||
Realm: d927fbf66df5532cfafc08afb5f7e53ded37b894
|
||||
WebRTC: f2a6203584745fe53532633397557876b5d71640
|
||||
|
||||
PODFILE CHECKSUM: a3ec9e9795d74a34aeaa86fdbc1983a365e93e9a
|
||||
PODFILE CHECKSUM: ba3cdb1f77ff6463e50f031aefd807535f5612ad
|
||||
|
||||
COCOAPODS: 1.4.0
|
||||
COCOAPODS: 1.5.0
|
||||
|
||||
Executable → Regular
+4
-123
@@ -70,11 +70,11 @@
|
||||
327382C41F276AED00356143 /* Vector.strings in Resources */ = {isa = PBXBuildFile; fileRef = 327382C01F276AED00356143 /* Vector.strings */; };
|
||||
32795C0B203DA4C4002420E2 /* DisabledRoomInputToolbarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 32795C09203DA4C3002420E2 /* DisabledRoomInputToolbarView.m */; };
|
||||
32795C0C203DA4C4002420E2 /* DisabledRoomInputToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32795C0A203DA4C4002420E2 /* DisabledRoomInputToolbarView.xib */; };
|
||||
3284A35120A07C210044F922 /* postMessageAPI.js in Resources */ = {isa = PBXBuildFile; fileRef = 3284A35020A07C210044F922 /* postMessageAPI.js */; };
|
||||
3284D7FF1FBB34B70090AA80 /* RoomKeyRequestViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3284D7FE1FBB34B70090AA80 /* RoomKeyRequestViewController.m */; };
|
||||
32918EA91F473BDB0076CA16 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 32918EA51F473BDB0076CA16 /* Localizable.strings */; };
|
||||
32918EAA1F473BDB0076CA16 /* Vector.strings in Resources */ = {isa = PBXBuildFile; fileRef = 32918EA71F473BDB0076CA16 /* Vector.strings */; };
|
||||
32935CB11F6056FD006888C8 /* IntegrationManagerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 32935CB01F6056FD006888C8 /* IntegrationManagerViewController.m */; };
|
||||
32935CB41F628BCE006888C8 /* IntegrationManager.js in Resources */ = {isa = PBXBuildFile; fileRef = 32935CB31F628BCE006888C8 /* IntegrationManager.js */; };
|
||||
329DCE191F988F8100468420 /* riot_icon_callkit.png in Resources */ = {isa = PBXBuildFile; fileRef = 329DCE161F988F8100468420 /* riot_icon_callkit.png */; };
|
||||
329DCE1A1F988F8100468420 /* riot_icon_callkit@2.png in Resources */ = {isa = PBXBuildFile; fileRef = 329DCE171F988F8100468420 /* riot_icon_callkit@2.png */; };
|
||||
329DCE1B1F988F8100468420 /* riot_icon_callkit@3.png in Resources */ = {isa = PBXBuildFile; fileRef = 329DCE181F988F8100468420 /* riot_icon_callkit@3.png */; };
|
||||
@@ -733,13 +733,13 @@
|
||||
32795C08203DA4C3002420E2 /* DisabledRoomInputToolbarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DisabledRoomInputToolbarView.h; sourceTree = "<group>"; };
|
||||
32795C09203DA4C3002420E2 /* DisabledRoomInputToolbarView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DisabledRoomInputToolbarView.m; sourceTree = "<group>"; };
|
||||
32795C0A203DA4C4002420E2 /* DisabledRoomInputToolbarView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DisabledRoomInputToolbarView.xib; sourceTree = "<group>"; };
|
||||
3284A35020A07C210044F922 /* postMessageAPI.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = postMessageAPI.js; sourceTree = "<group>"; };
|
||||
3284D7FD1FBB34B70090AA80 /* RoomKeyRequestViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RoomKeyRequestViewController.h; sourceTree = "<group>"; };
|
||||
3284D7FE1FBB34B70090AA80 /* RoomKeyRequestViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RoomKeyRequestViewController.m; sourceTree = "<group>"; };
|
||||
32918EA61F473BDB0076CA16 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = Localizable.strings; sourceTree = "<group>"; };
|
||||
32918EA81F473BDB0076CA16 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = Vector.strings; sourceTree = "<group>"; };
|
||||
32935CAF1F6056FD006888C8 /* IntegrationManagerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntegrationManagerViewController.h; sourceTree = "<group>"; };
|
||||
32935CB01F6056FD006888C8 /* IntegrationManagerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IntegrationManagerViewController.m; sourceTree = "<group>"; };
|
||||
32935CB31F628BCE006888C8 /* IntegrationManager.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = IntegrationManager.js; sourceTree = "<group>"; };
|
||||
329DCE161F988F8100468420 /* riot_icon_callkit.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = riot_icon_callkit.png; sourceTree = "<group>"; };
|
||||
329DCE171F988F8100468420 /* riot_icon_callkit@2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "riot_icon_callkit@2.png"; sourceTree = "<group>"; };
|
||||
329DCE181F988F8100468420 /* riot_icon_callkit@3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "riot_icon_callkit@3.png"; sourceTree = "<group>"; };
|
||||
@@ -1627,7 +1627,7 @@
|
||||
32935CB21F628B98006888C8 /* js */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
32935CB31F628BCE006888C8 /* IntegrationManager.js */,
|
||||
3284A35020A07C210044F922 /* postMessageAPI.js */,
|
||||
);
|
||||
path = js;
|
||||
sourceTree = "<group>";
|
||||
@@ -2737,7 +2737,6 @@
|
||||
24CBEC4A1F0EAD310093EABB /* Sources */,
|
||||
24CBEC4B1F0EAD310093EABB /* Frameworks */,
|
||||
24CBEC4C1F0EAD310093EABB /* Resources */,
|
||||
8EA19F5011654D3BD5EDAC33 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -2756,7 +2755,6 @@
|
||||
92726A3F1F58737A004AD26F /* Sources */,
|
||||
92726A401F58737A004AD26F /* Frameworks */,
|
||||
92726A411F58737A004AD26F /* Resources */,
|
||||
807A0ABF153A23C2FC22F977 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -2777,7 +2775,6 @@
|
||||
F094A9A01B78D8F000B1FBBF /* Resources */,
|
||||
24CBEC5D1F0EAD310093EABB /* Embed App Extensions */,
|
||||
7FFD40AA75DB32D83350D225 /* [CP] Embed Pods Frameworks */,
|
||||
68D6013FA64A4507DC9DB95B /* [CP] Copy Pods Resources */,
|
||||
3233F7481F3497E2006ACA81 /* Embed Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
@@ -3273,7 +3270,6 @@
|
||||
F083BD961E7009ED00A9B29C /* leave@2x.png in Resources */,
|
||||
F0E05A461EA0F9EB004B83FB /* tab_rooms@2x.png in Resources */,
|
||||
32AE61F41F0D2183007255F4 /* Vector.strings in Resources */,
|
||||
32935CB41F628BCE006888C8 /* IntegrationManager.js in Resources */,
|
||||
F083BD851E7009ED00A9B29C /* favouriteOff@3x.png in Resources */,
|
||||
F083BD541E7009ED00A9B29C /* camera_switch@2x.png in Resources */,
|
||||
F07CB3992029B20100C29C20 /* GroupTableViewCellWithSwitch.xib in Resources */,
|
||||
@@ -3295,6 +3291,7 @@
|
||||
327382C41F276AED00356143 /* Vector.strings in Resources */,
|
||||
32BB89EF204D86DA002F3AEC /* Localizable.strings in Resources */,
|
||||
F083BDE51E7009ED00A9B29C /* voice_call_icon@3x.png in Resources */,
|
||||
3284A35120A07C210044F922 /* postMessageAPI.js in Resources */,
|
||||
322806A11F0F64C4008C53D7 /* RoomMembershipExpandedBubbleCell.xib in Resources */,
|
||||
F083BE201E7009ED00A9B29C /* RoomParticipantsViewController.xib in Resources */,
|
||||
F083BE471E7009ED00A9B29C /* RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.xib in Resources */,
|
||||
@@ -3373,86 +3370,6 @@
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
68D6013FA64A4507DC9DB95B /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${SRCROOT}/Pods/Target Support Files/Pods-RiotPods-Riot/Pods-RiotPods-Riot-resources.sh",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKAccountDetailsViewController.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKAttachmentsViewController.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKAuthenticationViewController.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKCallViewController.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKContactDetailsViewController.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKContactListViewController.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKCountryPickerViewController.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKGroupListViewController.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKLanguagePickerViewController.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKRecentListViewController.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKRoomMemberDetailsViewController.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKRoomMemberListViewController.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKRoomSettingsViewController.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKRoomViewController.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Controllers/MXKSearchViewController.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/Account/MXKAccountTableViewCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/Authentication/MXKAuthInputsEmailCodeBasedView.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/Authentication/MXKAuthInputsPasswordBasedView.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/Contact/MXKContactTableCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/DeviceView/MXKDeviceView.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/EncryptionInfoView/MXKEncryptionInfoView.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/Group/MXKGroupTableViewCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKCollectionViewCell/MXKMediaCollectionViewCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKEventDetailsView.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKPieChartHUD.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKRoomCreationView.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithButton.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndButton.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndImageView.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndMXKImageView.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndSlider.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndSubLabel.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndSwitch.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndTextField.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelTextFieldAndButton.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithPicker.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithSearchBar.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithTextFieldAndButton.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithTextView.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/PushRule/MXKPushRuleCreationTableViewCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/PushRule/MXKPushRuleTableViewCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/ReadReceipts/MXKReadReceiptTableViewCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomEmptyBubbleTableViewCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomIncomingAttachmentBubbleCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomIncomingTextMsgBubbleCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomIOSOutgoingBubbleTableViewCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomOutgoingAttachmentBubbleCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomOutgoingTextMsgBubbleCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomBubbleList/MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarView.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarViewWithHPGrowingText.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarViewWithSimpleTextView.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomList/MXKInterleavedRecentTableViewCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomList/MXKPublicRoomTableViewCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomList/MXKRecentTableViewCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomMemberList/MXKRoomMemberTableViewCell.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomTitle/MXKRoomTitleView.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/RoomTitle/MXKRoomTitleViewWithTopic.xib",
|
||||
"${PODS_ROOT}/MatrixKit/MatrixKit/Views/Search/MXKSearchTableViewCell.xib",
|
||||
$PODS_CONFIGURATION_BUILD_DIR/MatrixKit/MatrixKit.bundle,
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RiotPods-Riot/Pods-RiotPods-Riot-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
6AA0024D4D5FAE30C2E1F311 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -3517,42 +3434,6 @@
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RiotPods-Riot/Pods-RiotPods-Riot-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
807A0ABF153A23C2FC22F977 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${SRCROOT}/Pods/Target Support Files/Pods-RiotPods-SiriIntents/Pods-RiotPods-SiriIntents-resources.sh",
|
||||
"${PODS_ROOT}/MatrixSDK/MatrixSDK/Data/Store/MXCoreDataStore/MXCoreDataStore.xcdatamodeld",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RiotPods-SiriIntents/Pods-RiotPods-SiriIntents-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
8EA19F5011654D3BD5EDAC33 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${SRCROOT}/Pods/Target Support Files/Pods-RiotPods-RiotShareExtension/Pods-RiotPods-RiotShareExtension-resources.sh",
|
||||
"${PODS_ROOT}/MatrixSDK/MatrixSDK/Data/Store/MXCoreDataStore/MXCoreDataStore.xcdatamodeld",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RiotPods-RiotShareExtension/Pods-RiotPods-RiotShareExtension-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
|
||||
@@ -74,6 +74,9 @@ extern NSString *const kAppDelegateNetworkStatusDidChangeNotification;
|
||||
// Current selected room id. nil if no room is presently visible.
|
||||
@property (strong, nonatomic) NSString *visibleRoomId;
|
||||
|
||||
// New message sound id.
|
||||
@property (nonatomic, readonly) SystemSoundID messageSound;
|
||||
|
||||
+ (AppDelegate*)theDelegate;
|
||||
|
||||
#pragma mark - Application layout handling
|
||||
|
||||
+94
-6
@@ -46,6 +46,8 @@
|
||||
|
||||
#include <MatrixSDK/MXUIKitBackgroundModeHandler.h>
|
||||
|
||||
#import "WebViewViewController.h"
|
||||
|
||||
@import PiwikTracker;
|
||||
|
||||
// Calls
|
||||
@@ -210,6 +212,9 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
|
||||
@property (strong, nonatomic) UIAlertController *logoutConfirmation;
|
||||
|
||||
@property (weak, nonatomic) UIAlertController *gdprConsentNotGivenAlertController;
|
||||
@property (weak, nonatomic) UIViewController *gdprConsentViewController;
|
||||
|
||||
@property (nonatomic, nullable, copy) void (^registrationForRemoteNotificationsCompletion)(NSError *);
|
||||
|
||||
|
||||
@@ -343,6 +348,10 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
|
||||
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(nullable NSDictionary *)launchOptions
|
||||
{
|
||||
// Create message sound
|
||||
NSURL *messageSoundURL = [[NSBundle mainBundle] URLForResource:@"message" withExtension:@"mp3"];
|
||||
AudioServicesCreateSystemSoundID((__bridge CFURLRef)messageSoundURL, &_messageSound);
|
||||
|
||||
NSLog(@"[AppDelegate] willFinishLaunchingWithOptions: Done");
|
||||
|
||||
return YES;
|
||||
@@ -592,6 +601,9 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
|
||||
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
||||
|
||||
// Register to GDPR consent not given notification
|
||||
[self registerUserConsentNotGivenNotification];
|
||||
|
||||
// Start monitoring reachability
|
||||
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
|
||||
|
||||
@@ -1464,7 +1476,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
{
|
||||
NSString *soundName = action.parameters[@"value"];
|
||||
if ([soundName isEqualToString:@"default"])
|
||||
soundName = UILocalNotificationDefaultSoundName;
|
||||
soundName = @"message.mp3";
|
||||
|
||||
eventNotification.soundName = soundName;
|
||||
}
|
||||
@@ -2196,7 +2208,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
sdkOptions.backgroundModeHandler = [[MXUIKitBackgroundModeHandler alloc] init];
|
||||
|
||||
// Get modular widget events in rooms histories
|
||||
[[MXKAppSettings standardAppSettings] addSupportedEventTypes:@[kWidgetEventTypeString]];
|
||||
[[MXKAppSettings standardAppSettings] addSupportedEventTypes:@[kWidgetMatrixEventTypeString, kWidgetModularEventTypeString]];
|
||||
|
||||
// Disable long press on event in bubble cells
|
||||
[MXKRoomBubbleTableViewCell disableLongPressGestureOnEvent:YES];
|
||||
@@ -2255,9 +2267,10 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
// Each room member will be considered as a potential contact.
|
||||
[MXKContactManager sharedManager].contactManagerMXRoomSource = MXKContactManagerMXRoomSourceAll;
|
||||
|
||||
// Send read receipts for modular widgets events too
|
||||
// Send read receipts for widgets events too
|
||||
NSMutableArray<MXEventTypeString> *acknowledgableEventTypes = [NSMutableArray arrayWithArray:mxSession.acknowledgableEventTypes];
|
||||
[acknowledgableEventTypes addObject:kWidgetEventTypeString];
|
||||
[acknowledgableEventTypes addObject:kWidgetMatrixEventTypeString];
|
||||
[acknowledgableEventTypes addObject:kWidgetModularEventTypeString];
|
||||
mxSession.acknowledgableEventTypes = acknowledgableEventTypes;
|
||||
}
|
||||
else if (mxSession.state == MXSessionStateStoreDataReady)
|
||||
@@ -3056,8 +3069,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
{
|
||||
if ([[ruleAction.parameters valueForKey:@"set_tweak"] isEqualToString:@"sound"])
|
||||
{
|
||||
// Play system sound (VoicemailReceived)
|
||||
AudioServicesPlaySystemSound (1002);
|
||||
// Play message sound
|
||||
AudioServicesPlaySystemSound(_messageSound);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3974,4 +3987,79 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - GDPR consent
|
||||
|
||||
// Observe user GDPR consent not given
|
||||
- (void)registerUserConsentNotGivenNotification
|
||||
{
|
||||
[NSNotificationCenter.defaultCenter addObserverForName:kMXHTTPClientUserConsentNotGivenErrorNotification
|
||||
object:nil
|
||||
queue:[NSOperationQueue mainQueue]
|
||||
usingBlock:^(NSNotification *notification)
|
||||
{
|
||||
NSString *consentURI = notification.userInfo[kMXHTTPClientUserConsentNotGivenErrorNotificationConsentURIKey];
|
||||
if (consentURI
|
||||
&& self.gdprConsentNotGivenAlertController == nil
|
||||
&& self.gdprConsentViewController == nil)
|
||||
{
|
||||
UIViewController *presentingViewController = self.window.rootViewController.presentedViewController ?: self.window.rootViewController;
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
|
||||
MXSession *mainSession = self.mxSessions.firstObject;
|
||||
NSString *homeServerName = mainSession.matrixRestClient.credentials.homeServerName;
|
||||
|
||||
NSString *alertMessage = [NSString stringWithFormat:NSLocalizedStringFromTable(@"gdpr_consent_not_given_alert_message", @"Vector", nil), homeServerName];
|
||||
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"settings_term_conditions", @"Vector", nil)
|
||||
message:alertMessage
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"gdpr_consent_not_given_alert_review_now_action", @"Vector", nil)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
typeof(weakSelf) strongSelf = weakSelf;
|
||||
|
||||
if (strongSelf)
|
||||
{
|
||||
[strongSelf presentGDPRConsentFromViewController:presentingViewController consentURI:consentURI];
|
||||
}
|
||||
}]];
|
||||
|
||||
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"later", @"Vector", nil)
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:nil]];
|
||||
|
||||
[presentingViewController presentViewController:alert animated:YES completion:nil];
|
||||
|
||||
self.gdprConsentNotGivenAlertController = alert;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)presentGDPRConsentFromViewController:(UIViewController*)viewController consentURI:(NSString*)consentURI
|
||||
{
|
||||
WebViewViewController *webViewViewController = [[WebViewViewController alloc] initWithURL:consentURI];
|
||||
webViewViewController.title = NSLocalizedStringFromTable(@"settings_term_conditions", @"Vector", nil);
|
||||
|
||||
UIBarButtonItem *closeBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:[NSBundle mxk_localizedStringForKey:@"close"]
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:self
|
||||
action:@selector(dismissGDPRConsent)];
|
||||
|
||||
webViewViewController.navigationItem.rightBarButtonItem = closeBarButtonItem;
|
||||
|
||||
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:webViewViewController];
|
||||
|
||||
[viewController presentViewController:navigationController animated:YES completion:nil];
|
||||
|
||||
self.gdprConsentViewController = navigationController;
|
||||
}
|
||||
|
||||
- (void)dismissGDPRConsent
|
||||
{
|
||||
[self.gdprConsentViewController dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -593,3 +593,6 @@
|
||||
"e2e_room_key_request_share_without_verifying" = "Share without verifying";
|
||||
"e2e_room_key_request_ignore_request" = "Ignore request";
|
||||
|
||||
// GDPR
|
||||
"gdpr_consent_not_given_alert_message" = "To continue using the %@ homeserver you must review and agree to the terms and conditions.";
|
||||
"gdpr_consent_not_given_alert_review_now_action" = "Review now";
|
||||
|
||||
@@ -28,7 +28,7 @@ window.riotIOS.sendObjectMessageToObjC = function(parameters) {
|
||||
|
||||
window.riotIOS.events = {};
|
||||
|
||||
// Listen to messages posted by Modular
|
||||
// Listen to messages posted by the widget
|
||||
window.riotIOS.onMessage = function(event) {
|
||||
|
||||
// Do not SPAM ObjC with event already managed
|
||||
@@ -40,6 +40,19 @@ window.riotIOS.onMessage = function(event) {
|
||||
event.origin = event.originalEvent.origin;
|
||||
}
|
||||
|
||||
// Use an internal "_id" field for matching onMessage events and requests
|
||||
// _id was originally used by the Modular API. Keep it
|
||||
if (!event.data._id) {
|
||||
// The Matrix Widget API v2 spec says:
|
||||
// "The requestId field should be unique and included in all requests"
|
||||
event.data._id = event.data.requestId;
|
||||
}
|
||||
|
||||
// Make sure to have one id
|
||||
if (!event.data._id) {
|
||||
event.data._id = Date.now() + "-" + Math.random().toString(36);
|
||||
}
|
||||
|
||||
// Keep this event for future usage
|
||||
riotIOS.events[event.data._id] = event;
|
||||
|
||||
@@ -50,7 +63,7 @@ window.riotIOS.onMessage = function(event) {
|
||||
window.addEventListener('message', riotIOS.onMessage, false);
|
||||
|
||||
|
||||
// ObjC -> Modular JS bridge
|
||||
// ObjC -> Widget JS bridge
|
||||
window.riotIOS.sendResponse = function(eventId, res) {
|
||||
|
||||
// Retrieve the correspong JS event
|
||||
+2
-2
@@ -17,11 +17,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.6.15</string>
|
||||
<string>0.6.16</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.6.15</string>
|
||||
<string>0.6.16</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<true/>
|
||||
<key>ITSEncryptionExportComplianceCode</key>
|
||||
|
||||
+10
-18
@@ -27,11 +27,6 @@
|
||||
The calendar used to retrieve the today date.
|
||||
*/
|
||||
NSCalendar *calendar;
|
||||
|
||||
/**
|
||||
The local time zone
|
||||
*/
|
||||
NSTimeZone *localTimeZone;
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -39,10 +34,10 @@
|
||||
|
||||
- (NSAttributedString *)attributedStringFromEvent:(MXEvent *)event withRoomState:(MXRoomState *)roomState error:(MXKEventFormatterError *)error
|
||||
{
|
||||
// Build strings for modular widget events
|
||||
// TODO: At the moment, we support only jitsi widgets
|
||||
// Build strings for widget events
|
||||
if (event.eventType == MXEventTypeCustom
|
||||
&& [event.type isEqualToString:kWidgetEventTypeString])
|
||||
&& ([event.type isEqualToString:kWidgetMatrixEventTypeString]
|
||||
|| [event.type isEqualToString:kWidgetModularEventTypeString]))
|
||||
{
|
||||
NSString *displayText;
|
||||
|
||||
@@ -71,7 +66,11 @@
|
||||
// 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])
|
||||
// Get all widgets state events in the room
|
||||
NSMutableArray<MXEvent*> *widgetStateEvents = [NSMutableArray arrayWithArray:[roomState stateEventsWithType:kWidgetMatrixEventTypeString]];
|
||||
[widgetStateEvents addObjectsFromArray:[roomState stateEventsWithType:kWidgetModularEventTypeString]];
|
||||
|
||||
for (MXEvent *widgetStateEvent in widgetStateEvents)
|
||||
{
|
||||
if ([widgetStateEvent.stateKey isEqualToString:widget.widgetId])
|
||||
{
|
||||
@@ -140,10 +139,6 @@
|
||||
if (self)
|
||||
{
|
||||
calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
|
||||
// Note: NSDate object always shows time according to GMT, so the calendar should be in GMT too.
|
||||
calendar.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
|
||||
|
||||
localTimeZone = [NSTimeZone localTimeZone];
|
||||
|
||||
// Use the secondary bg color to set the background color in the default CSS.
|
||||
NSUInteger bgColor = [MXKTools rgbValueWithColor:kRiotSecondaryBgColor];
|
||||
@@ -453,12 +448,9 @@
|
||||
}
|
||||
|
||||
// Retrieve today date at midnight
|
||||
NSDateComponents *components = [calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:[NSDate date]];
|
||||
NSDate *today = [calendar dateFromComponents:components];
|
||||
NSDate *today = [calendar startOfDayForDate:[NSDate date]];
|
||||
|
||||
NSTimeInterval localZoneOffset = [localTimeZone secondsFromGMT];
|
||||
|
||||
NSTimeInterval interval = -[date timeIntervalSinceDate:today] - localZoneOffset;
|
||||
NSTimeInterval interval = -[date timeIntervalSinceDate:today];
|
||||
|
||||
if (interval > 60*60*24*364)
|
||||
{
|
||||
|
||||
@@ -22,9 +22,12 @@
|
||||
|
||||
- (instancetype)initWithWidgetEvent:(MXEvent *)widgetEvent inMatrixSession:(MXSession*)mxSession
|
||||
{
|
||||
if (![widgetEvent.type isEqualToString:kWidgetEventTypeString])
|
||||
// TODO - Room widgets need to be moved to 'm.widget' state events
|
||||
// https://docs.google.com/document/d/1uPF7XWY_dXTKVKV7jZQ2KmsI19wn9-kFRgQ1tFQP7wQ/edit?usp=sharing
|
||||
if (![widgetEvent.type isEqualToString:kWidgetMatrixEventTypeString]
|
||||
&& ![widgetEvent.type isEqualToString:kWidgetModularEventTypeString])
|
||||
{
|
||||
// The Widget class works only with modular, aka "im.vector.modular.widgets", widgets
|
||||
// The Widget class works only with modular, aka "m.widget" or "im.vector.modular.widgets", widgets
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -67,9 +70,6 @@
|
||||
widgetUrl = [widgetUrl stringByReplacingOccurrencesOfString:@"$matrix_display_name" withString:displayName];
|
||||
widgetUrl = [widgetUrl stringByReplacingOccurrencesOfString:@"$matrix_avatar_url" withString:avatarUrl];
|
||||
|
||||
// And add the user scalar token
|
||||
widgetUrl = [widgetUrl stringByAppendingString:[NSString stringWithFormat:@"&scalar_token=%@", scalarToken]];
|
||||
|
||||
// Integrate widget data into widget url
|
||||
for (NSString *key in _data)
|
||||
{
|
||||
@@ -97,6 +97,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Add the user scalar token
|
||||
widgetUrl = [widgetUrl stringByAppendingString:[NSString stringWithFormat:@"%@scalar_token=%@",
|
||||
[widgetUrl containsString:@"?"] ? @"&" : @"?",
|
||||
scalarToken]];
|
||||
|
||||
// Add the widget id
|
||||
widgetUrl = [widgetUrl stringByAppendingString:[NSString stringWithFormat:@"&widgetId=%@", _widgetId]];
|
||||
|
||||
success(widgetUrl);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,9 +21,15 @@
|
||||
#import "Widget.h"
|
||||
|
||||
/**
|
||||
The type of matrix event used for modular widgets.
|
||||
The type of matrix event used for matrix widgets.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString *const kWidgetEventTypeString;
|
||||
FOUNDATION_EXPORT NSString *const kWidgetMatrixEventTypeString;
|
||||
|
||||
/**
|
||||
The type of matrix event used for modular widgets.
|
||||
TODO: It should be replaced by kWidgetMatrixEventTypeString.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString *const kWidgetModularEventTypeString;
|
||||
|
||||
/**
|
||||
Known types widgets.
|
||||
@@ -87,6 +93,14 @@ WidgetManagerErrorCode;
|
||||
*/
|
||||
- (NSArray<Widget*> *)widgetsNotOfTypes:(NSArray<NSString*>*)notWidgetTypes inRoom:(MXRoom*)room;
|
||||
|
||||
/**
|
||||
List all widgets of an account.
|
||||
|
||||
@param mxSession the session of the user account.
|
||||
@return a list of widgets.
|
||||
*/
|
||||
- (NSArray<Widget*> *)userWidgets:(MXSession*)mxSession;
|
||||
|
||||
/**
|
||||
Add a modular widget to a room.
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
|
||||
#pragma mark - Contants
|
||||
|
||||
NSString *const kWidgetEventTypeString = @"im.vector.modular.widgets";
|
||||
NSString *const kWidgetMatrixEventTypeString = @"m.widget";
|
||||
NSString *const kWidgetModularEventTypeString = @"im.vector.modular.widgets";
|
||||
NSString *const kWidgetTypeJitsi = @"jitsi";
|
||||
|
||||
NSString *const kWidgetManagerDidUpdateWidgetNotification = @"kWidgetManagerDidUpdateWidgetNotification";
|
||||
@@ -102,10 +103,11 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
|
||||
// Widget id -> widget
|
||||
NSMutableDictionary <NSString*, Widget *> *widgets = [NSMutableDictionary dictionary];
|
||||
|
||||
// Get all im.vector.modular.widgets state events in the room
|
||||
NSMutableArray<MXEvent*> *widgetEvents = [NSMutableArray arrayWithArray:[room.state stateEventsWithType:kWidgetEventTypeString]];
|
||||
// Get all widgets state events in the room
|
||||
NSMutableArray<MXEvent*> *widgetEvents = [NSMutableArray arrayWithArray:[room.state stateEventsWithType:kWidgetMatrixEventTypeString]];
|
||||
[widgetEvents addObjectsFromArray:[room.state stateEventsWithType:kWidgetModularEventTypeString]];
|
||||
|
||||
// There can be several im.vector.modular.widgets state events for a same widget but
|
||||
// There can be several widgets state events for a same widget but
|
||||
// only the last one must be considered.
|
||||
|
||||
// Order widgetEvents with the last event first
|
||||
@@ -124,7 +126,7 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
|
||||
return result;
|
||||
}];
|
||||
|
||||
// Create each widget from its lastest im.vector.modular.widgets state event
|
||||
// Create each widget from its lastest widgets state event
|
||||
for (MXEvent *widgetEvent in widgetEvents)
|
||||
{
|
||||
// Filter widget types if required
|
||||
@@ -170,6 +172,39 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
|
||||
return activeWidgets;
|
||||
}
|
||||
|
||||
- (NSArray<Widget*> *)userWidgets:(MXSession*)mxSession
|
||||
{
|
||||
// Disable user widgets (sticker picker) for now
|
||||
return nil;
|
||||
|
||||
// Get all widgets in the user account data
|
||||
NSMutableArray<Widget *> *userWidgets = [NSMutableArray array];
|
||||
for (NSDictionary *widgetEventContent in [mxSession.accountData accountDataForEventType:@"m.widgets"].allValues)
|
||||
{
|
||||
// Patch: Modular uses a malformed key: "stateKey" instead of "state_key"
|
||||
// TODO: To remove once fixed server side
|
||||
NSDictionary *widgetEventContentFixed = widgetEventContent;
|
||||
if (!widgetEventContent[@"state_key"] && widgetEventContent[@"stateKey"])
|
||||
{
|
||||
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:widgetEventContent];
|
||||
dict[@"state_key"] = widgetEventContent[@"stateKey"];
|
||||
widgetEventContentFixed = dict;
|
||||
}
|
||||
|
||||
MXEvent *widgetEvent = [MXEvent modelFromJSON:widgetEventContentFixed];
|
||||
if (widgetEvent)
|
||||
{
|
||||
Widget *widget = [[Widget alloc] initWithWidgetEvent:widgetEvent inMatrixSession:mxSession];
|
||||
if (widget)
|
||||
{
|
||||
[userWidgets addObject:widget];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return userWidgets;
|
||||
}
|
||||
|
||||
- (MXHTTPOperation *)createWidget:(NSString*)widgetId
|
||||
withContent:(NSDictionary<NSString*, NSObject*>*)widgetContent
|
||||
inRoom:(MXRoom*)room
|
||||
@@ -192,7 +227,8 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
|
||||
|
||||
// Send a state event with the widget data
|
||||
// TODO: This API will be shortly replaced by a pure modular API
|
||||
return [room sendStateEventOfType:kWidgetEventTypeString
|
||||
// TODO: Move to kWidgetMatrixEventTypeString ("m.widget") type but when?
|
||||
return [room sendStateEventOfType:kWidgetModularEventTypeString
|
||||
content:widgetContent
|
||||
stateKey:widgetId
|
||||
success:nil failure:failure];
|
||||
@@ -247,7 +283,8 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
|
||||
|
||||
// Send a state event with an empty content to disable the widget
|
||||
// TODO: This API will be shortly replaced by a pure modular API
|
||||
return [room sendStateEventOfType:kWidgetEventTypeString
|
||||
// TODO: Move to kWidgetMatrixEventTypeString ("m.widget") type but when?
|
||||
return [room sendStateEventOfType:kWidgetModularEventTypeString
|
||||
content:@{}
|
||||
stateKey:widgetId
|
||||
success:^(NSString *eventId)
|
||||
@@ -292,7 +329,7 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
|
||||
|
||||
NSString *hash = [NSString stringWithFormat:@"%p", mxSession];
|
||||
|
||||
id listener = [mxSession listenToEventsOfTypes:@[kWidgetEventTypeString] onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) {
|
||||
id listener = [mxSession listenToEventsOfTypes:@[kWidgetMatrixEventTypeString, kWidgetModularEventTypeString] onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) {
|
||||
|
||||
typeof(self) self = weakSelf;
|
||||
|
||||
|
||||
@@ -1188,7 +1188,9 @@ static void *RecordingContext = &RecordingContext;
|
||||
}
|
||||
}
|
||||
|
||||
self.cameraSwitchButton.hidden = (!frontCamera || !backCamera);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.cameraSwitchButton.hidden = (!frontCamera || !backCamera);
|
||||
});
|
||||
|
||||
if (currentCameraInput)
|
||||
{
|
||||
|
||||
@@ -1257,9 +1257,10 @@
|
||||
// If the setting is disabled, do not show the icon
|
||||
self.navigationItem.rightBarButtonItems = @[self.navigationItem.rightBarButtonItem];
|
||||
}
|
||||
else if (self.widgetsCount)
|
||||
else if ([self widgetsCount:NO])
|
||||
{
|
||||
// Show there are widgets by changing the "apps" icon color
|
||||
// Show it in red only for room widgets, not user's widgets
|
||||
// TODO: Design must be reviewed
|
||||
UIImage *icon = self.navigationItem.rightBarButtonItems[1].image;
|
||||
icon = [MXKTools paintImage:icon withColor:kRiotColorPinkRed];
|
||||
@@ -3063,7 +3064,7 @@
|
||||
// Matrix Apps button
|
||||
else if (self.navigationItem.rightBarButtonItems.count == 2 && sender == self.navigationItem.rightBarButtonItems[1])
|
||||
{
|
||||
if (self.widgetsCount)
|
||||
if ([self widgetsCount:YES])
|
||||
{
|
||||
WidgetPickerViewController *widgetPicker = [[WidgetPickerViewController alloc] initForMXSession:self.roomDataSource.mxSession
|
||||
inRoom:self.roomDataSource.roomId];
|
||||
@@ -3643,10 +3644,16 @@
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
}
|
||||
|
||||
- (NSUInteger)widgetsCount
|
||||
- (NSUInteger)widgetsCount:(BOOL)includeUserWidgets
|
||||
{
|
||||
return [[WidgetManager sharedManager] widgetsNotOfTypes:@[kWidgetTypeJitsi]
|
||||
inRoom:self.roomDataSource.room].count;
|
||||
NSUInteger widgetsCount = [[WidgetManager sharedManager] widgetsNotOfTypes:@[kWidgetTypeJitsi]
|
||||
inRoom:self.roomDataSource.room].count;
|
||||
if (includeUserWidgets)
|
||||
{
|
||||
widgetsCount = [[WidgetManager sharedManager] userWidgets:self.roomDataSource.room.mxSession].count;
|
||||
}
|
||||
|
||||
return widgetsCount;
|
||||
}
|
||||
|
||||
#pragma mark - Unreachable Network Handling
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#import "WebViewViewController.h"
|
||||
|
||||
#import <MatrixSDK/MatrixSDK.h>
|
||||
#import "WidgetViewController.h"
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kIntegrationManagerMainScreen;
|
||||
FOUNDATION_EXPORT NSString *const kIntegrationManagerAddIntegrationScreen;
|
||||
@@ -24,8 +22,10 @@ FOUNDATION_EXPORT NSString *const kIntegrationManagerAddIntegrationScreen;
|
||||
/**
|
||||
`IntegrationManagerViewController` displays the Modular integration manager webapp
|
||||
into a webview.
|
||||
|
||||
It reuses the postMessage API pipe defined in `WidgetViewController`.
|
||||
*/
|
||||
@interface IntegrationManagerViewController : WebViewViewController <UIWebViewDelegate>
|
||||
@interface IntegrationManagerViewController : WidgetViewController
|
||||
|
||||
/**
|
||||
Initialise with params for the Modular interface webapp.
|
||||
|
||||
@@ -17,15 +17,10 @@
|
||||
#import "IntegrationManagerViewController.h"
|
||||
|
||||
#import "WidgetManager.h"
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import <JavaScriptCore/JavaScriptCore.h>
|
||||
|
||||
NSString *const kIntegrationManagerMainScreen = nil;
|
||||
NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
|
||||
|
||||
NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@', %@);";
|
||||
|
||||
|
||||
@interface IntegrationManagerViewController ()
|
||||
{
|
||||
@@ -63,19 +58,6 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
operation = nil;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
webView.scalesPageToFit = NO;
|
||||
webView.scrollView.bounces = NO;
|
||||
|
||||
// Disable opacity so that the webview background uses the current interface theme
|
||||
webView.opaque = NO;
|
||||
|
||||
webView.delegate = self;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
@@ -86,7 +68,7 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
|
||||
[self startActivityIndicator];
|
||||
|
||||
// Make sure we a scalar token
|
||||
// Make sure we have a scalar token
|
||||
operation = [[WidgetManager sharedManager] getScalarTokenForMXSession:mxSession success:^(NSString *theScalarToken) {
|
||||
|
||||
typeof(self) self = weakSelf;
|
||||
@@ -147,143 +129,15 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
return url;
|
||||
}
|
||||
|
||||
- (void)enableDebug
|
||||
#pragma mark - Modular postMessage API implementation
|
||||
|
||||
- (void)onPostMessageRequest:(NSString*)requestId data:(NSDictionary*)requestData
|
||||
{
|
||||
// Setup console.log() -> NSLog() route
|
||||
JSContext *ctx = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
|
||||
ctx[@"console"][@"log"] = ^(JSValue * msg) {
|
||||
NSLog(@"-- JavaScript: %@", msg);
|
||||
};
|
||||
|
||||
// Redirect all console.* logging methods to console.log
|
||||
[webView stringByEvaluatingJavaScriptFromString:@"console.debug = console.log; console.info = console.log; console.warn = console.log; console.error = console.log;"];
|
||||
}
|
||||
|
||||
- (void)showErrorAsAlert:(NSError*)error
|
||||
{
|
||||
NSString *title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey];
|
||||
NSString *msg = [error.userInfo valueForKey:NSLocalizedDescriptionKey];
|
||||
if (!title)
|
||||
{
|
||||
if (msg)
|
||||
{
|
||||
title = msg;
|
||||
msg = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
title = [NSBundle mxk_localizedStringForKey:@"error"];
|
||||
}
|
||||
}
|
||||
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
typeof(self) self = weakSelf;
|
||||
|
||||
if (self)
|
||||
{
|
||||
// Leave this Intergrations Manager VC
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - UIWebViewDelegate
|
||||
|
||||
-(void)webViewDidFinishLoad:(UIWebView *)theWebView
|
||||
{
|
||||
[self enableDebug];
|
||||
|
||||
// Setup js code
|
||||
NSString *path = [[NSBundle mainBundle] pathForResource:@"IntegrationManager" ofType:@"js"];
|
||||
NSString *js = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
|
||||
[webView stringByEvaluatingJavaScriptFromString:js];
|
||||
|
||||
[self stopActivityIndicator];
|
||||
|
||||
// Check connectivity
|
||||
if ([AppDelegate theDelegate].isOffline)
|
||||
{
|
||||
// The web page may be in the cache, so its loading will be successful
|
||||
// but we cannot go further, it often leads to a blank screen.
|
||||
// So, display an error so that the user can escape.
|
||||
NSError *error = [NSError errorWithDomain:NSURLErrorDomain
|
||||
code:NSURLErrorNotConnectedToInternet
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey : NSLocalizedStringFromTable(@"network_offline_prompt", @"Vector", nil)
|
||||
}];
|
||||
[self showErrorAsAlert:error];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
|
||||
{
|
||||
NSString *urlString = [[request URL] absoluteString];
|
||||
|
||||
if ([urlString hasPrefix:@"js:"])
|
||||
{
|
||||
// Listen only to scheme of the JS-UIWebView bridge
|
||||
NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
|
||||
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSError *error;
|
||||
NSDictionary *parameters = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers
|
||||
error:&error];
|
||||
if (!error)
|
||||
{
|
||||
[self onMessage:parameters];
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (navigationType == UIWebViewNavigationTypeLinkClicked )
|
||||
{
|
||||
// Open links outside the app
|
||||
[[UIApplication sharedApplication] openURL:[request URL]];
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
|
||||
{
|
||||
// Filter out the users's scalar token
|
||||
NSString *errorDescription = error.description;
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"scalar_token=\\w*"
|
||||
options:NSRegularExpressionCaseInsensitive error:nil];
|
||||
errorDescription = [regex stringByReplacingMatchesInString:errorDescription
|
||||
options:0
|
||||
range:NSMakeRange(0, errorDescription.length)
|
||||
withTemplate:@"scalar_token=..."];
|
||||
|
||||
NSLog(@"[IntegrationManagerVC] didFailLoadWithError: %@", errorDescription);
|
||||
|
||||
[self stopActivityIndicator];
|
||||
[self showErrorAsAlert:error];
|
||||
}
|
||||
|
||||
#pragma mark - Modular postMessage API
|
||||
|
||||
- (void)onMessage:(NSDictionary*)JSData
|
||||
{
|
||||
NSDictionary *eventData;
|
||||
MXJSONModelSetDictionary(eventData, JSData[@"event.data"]);
|
||||
|
||||
NSString *roomIdInEvent, *userId, *action;
|
||||
|
||||
MXJSONModelSetString(roomIdInEvent, eventData[@"room_id"]);
|
||||
MXJSONModelSetString(userId, eventData[@"user_id"]);
|
||||
MXJSONModelSetString(action, eventData[@"action"]);
|
||||
MXJSONModelSetString(roomIdInEvent, requestData[@"room_id"]);
|
||||
MXJSONModelSetString(userId, requestData[@"user_id"]);
|
||||
MXJSONModelSetString(action, requestData[@"action"]);
|
||||
|
||||
if ([action isEqualToString:@"close_scalar"])
|
||||
{
|
||||
@@ -293,13 +147,13 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
|
||||
if (!roomIdInEvent)
|
||||
{
|
||||
[self sendLocalisedError:@"widget_integration_missing_room_id" toEvent:eventData];
|
||||
[self sendLocalisedError:@"widget_integration_missing_room_id" toRequest:requestId];
|
||||
return;
|
||||
}
|
||||
|
||||
if (![roomIdInEvent isEqualToString:roomId])
|
||||
{
|
||||
[self sendError:[NSString stringWithFormat:NSLocalizedStringFromTable(@"widget_integration_room_not_visible", @"Vector", nil), roomIdInEvent] toEvent:eventData];
|
||||
[self sendError:[NSString stringWithFormat:NSLocalizedStringFromTable(@"widget_integration_room_not_visible", @"Vector", nil), roomIdInEvent] toRequest:requestId];
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -307,149 +161,86 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
// These APIs don't require userId
|
||||
if ([@"join_rules_state" isEqualToString:action])
|
||||
{
|
||||
[self getJoinRules:eventData];
|
||||
[self getJoinRules:requestId data:requestData];
|
||||
return;
|
||||
}
|
||||
else if ([@"set_plumbing_state" isEqualToString:action])
|
||||
{
|
||||
[self setPlumbingState:eventData];
|
||||
[self setPlumbingState:requestId data:requestData];
|
||||
return;
|
||||
}
|
||||
else if ([@"get_membership_count" isEqualToString:action])
|
||||
{
|
||||
[self getMembershipCount:eventData];
|
||||
[self getMembershipCount:requestId data:requestData];
|
||||
return;
|
||||
}
|
||||
else if ([@"set_widget" isEqualToString:action])
|
||||
{
|
||||
[self setWidget:eventData];
|
||||
[self setWidget:requestId data:requestData];
|
||||
return;
|
||||
}
|
||||
else if ([@"get_widgets" isEqualToString:action])
|
||||
{
|
||||
[self getWidgets:eventData];
|
||||
[self getWidgets:requestId data:requestData];
|
||||
return;
|
||||
}
|
||||
else if ([@"can_send_event" isEqualToString:action])
|
||||
{
|
||||
[self canSendEvent:eventData];
|
||||
[self canSendEvent:requestId data:requestData];
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!userId)
|
||||
{
|
||||
[self sendLocalisedError:@"widget_integration_missing_user_id" toEvent:eventData];
|
||||
[self sendLocalisedError:@"widget_integration_missing_user_id" toRequest:requestId];
|
||||
return;
|
||||
}
|
||||
|
||||
if ([@"membership_state" isEqualToString:action])
|
||||
{
|
||||
[self getMembershipState:userId eventData:eventData];
|
||||
[self getMembershipState:userId request:requestId data:requestData];
|
||||
}
|
||||
else if ([@"invite" isEqualToString:action])
|
||||
{
|
||||
[self inviteUser:userId eventData:eventData];
|
||||
[self inviteUser:userId request:requestId data:requestData];
|
||||
}
|
||||
else if ([@"bot_options" isEqualToString:action])
|
||||
{
|
||||
[self getBotOptions:userId eventData:eventData];
|
||||
[self getBotOptions:userId request:requestId data:requestData];
|
||||
}
|
||||
else if ([@"set_bot_options" isEqualToString:action])
|
||||
{
|
||||
[self setBotOptions:userId eventData:eventData];
|
||||
[self setBotOptions:userId request:requestId data:requestData];
|
||||
}
|
||||
else if ([@"set_bot_power" isEqualToString:action])
|
||||
{
|
||||
[self setBotPower:userId eventData:eventData];
|
||||
[self setBotPower:userId request:requestId data:requestData];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"[IntegrationManagerViewControllerVC] Unhandled postMessage event with action %@: %@", action, JSData);
|
||||
NSLog(@"[IntegrationManagerViewControllerVC] Unhandled postMessage event with action %@: %@", action, requestData);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendBoolResponse:(BOOL)response toEvent:(NSDictionary*)eventData
|
||||
{
|
||||
// Convert BOOL to "true" or "false"
|
||||
NSString *js = [NSString stringWithFormat:kJavascriptSendResponseToModular,
|
||||
eventData[@"_id"],
|
||||
response ? @"true" : @"false"];
|
||||
#pragma mark - Private methods
|
||||
|
||||
[webView stringByEvaluatingJavaScriptFromString:js];
|
||||
}
|
||||
|
||||
- (void)sendIntegerResponse:(NSUInteger)response toEvent:(NSDictionary*)eventData
|
||||
{
|
||||
NSString *js = [NSString stringWithFormat:kJavascriptSendResponseToModular,
|
||||
eventData[@"_id"],
|
||||
@(response)];
|
||||
|
||||
[webView stringByEvaluatingJavaScriptFromString:js];
|
||||
}
|
||||
|
||||
- (void)sendNSObjectResponse:(NSObject*)response toEvent:(NSDictionary*)eventData
|
||||
{
|
||||
NSString *jsString;
|
||||
|
||||
if (response)
|
||||
{
|
||||
// Convert response into a JS object through a JSON string
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:response
|
||||
options:0
|
||||
error:0];
|
||||
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
|
||||
jsString = [NSString stringWithFormat:@"JSON.parse('%@')", jsonString];
|
||||
}
|
||||
else
|
||||
{
|
||||
jsString = @"null";
|
||||
}
|
||||
|
||||
NSString *js = [NSString stringWithFormat:kJavascriptSendResponseToModular,
|
||||
eventData[@"_id"],
|
||||
jsString];
|
||||
|
||||
[webView stringByEvaluatingJavaScriptFromString:js];
|
||||
}
|
||||
|
||||
- (void)sendError:(NSString*)message toEvent:(NSDictionary*)eventData
|
||||
{
|
||||
NSLog(@"[IntegrationManagerVC] sendError: Action %@ failed with message: %@", eventData[@"action"], message);
|
||||
|
||||
// TODO: JS has an additional optional parameter: nestedError
|
||||
[self sendNSObjectResponse:@{
|
||||
@"error": @{
|
||||
@"message": message
|
||||
}
|
||||
}
|
||||
toEvent:eventData];
|
||||
}
|
||||
|
||||
- (void)sendLocalisedError:(NSString*)errorKey toEvent:(NSDictionary*)eventData
|
||||
{
|
||||
[self sendError:NSLocalizedStringFromTable(errorKey, @"Vector", nil) toEvent:eventData];
|
||||
}
|
||||
|
||||
#pragma mark - Modular postMessage Implementation
|
||||
|
||||
- (MXRoom *)roomCheckWithEvent:(NSDictionary*)eventData
|
||||
- (MXRoom *)roomCheckForRequest:(NSString*)requestId data:(NSDictionary*)requestData
|
||||
{
|
||||
MXRoom *room = [mxSession roomWithRoomId:roomId];
|
||||
if (!room)
|
||||
{
|
||||
[self sendLocalisedError:@"widget_integration_room_not_recognised" toEvent:eventData];
|
||||
[self sendLocalisedError:@"widget_integration_room_not_recognised" toRequest:requestId];
|
||||
}
|
||||
|
||||
return room;
|
||||
}
|
||||
|
||||
- (void)inviteUser:(NSString*)userId eventData:(NSDictionary*)eventData
|
||||
- (void)inviteUser:(NSString*)userId request:(NSString*)requestId data:(NSDictionary*)requestData
|
||||
{
|
||||
NSLog(@"[IntegrationManagerVC] Received request to invite %@ into room %@.", userId, roomId);
|
||||
|
||||
MXRoom *room = [self roomCheckWithEvent:eventData];
|
||||
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
|
||||
|
||||
if (room)
|
||||
{
|
||||
@@ -459,7 +250,7 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
[self sendNSObjectResponse:@{
|
||||
@"success": @(YES)
|
||||
}
|
||||
toEvent:eventData];
|
||||
toRequest:requestId];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -473,7 +264,7 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
[self sendNSObjectResponse:@{
|
||||
@"success": @(YES)
|
||||
}
|
||||
toEvent:eventData];
|
||||
toRequest:requestId];
|
||||
}
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
@@ -481,18 +272,18 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
typeof(self) self = weakSelf;
|
||||
if (self)
|
||||
{
|
||||
[self sendLocalisedError:@"widget_integration_need_to_be_able_to_invite" toEvent:eventData];
|
||||
[self sendLocalisedError:@"widget_integration_need_to_be_able_to_invite" toRequest:requestId];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setWidget:(NSDictionary*)eventData
|
||||
- (void)setWidget:(NSString*)requestId data:(NSDictionary*)requestData
|
||||
{
|
||||
NSLog(@"[IntegrationManagerVC] Received request to set widget in room %@.", roomId);
|
||||
|
||||
MXRoom *room = [self roomCheckWithEvent:eventData];
|
||||
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
|
||||
|
||||
if (room)
|
||||
{
|
||||
@@ -500,15 +291,15 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
NSString *widgetName; // optional
|
||||
NSDictionary *widgetData ; // optional
|
||||
|
||||
MXJSONModelSetString(widget_id, eventData[@"widget_id"]);
|
||||
MXJSONModelSetString(widgetType, eventData[@"type"]);
|
||||
MXJSONModelSetString(widgetUrl, eventData[@"url"]);
|
||||
MXJSONModelSetString(widgetName, eventData[@"name"]);
|
||||
MXJSONModelSetDictionary(widgetData, eventData[@"data"]);
|
||||
MXJSONModelSetString(widget_id, requestData[@"widget_id"]);
|
||||
MXJSONModelSetString(widgetType, requestData[@"type"]);
|
||||
MXJSONModelSetString(widgetUrl, requestData[@"url"]);
|
||||
MXJSONModelSetString(widgetName, requestData[@"name"]);
|
||||
MXJSONModelSetDictionary(widgetData, requestData[@"data"]);
|
||||
|
||||
if (!widget_id)
|
||||
{
|
||||
[self sendLocalisedError:@"widget_integration_unable_to_create" toEvent:eventData]; // new Error("Missing required widget fields."));
|
||||
[self sendLocalisedError:@"widget_integration_unable_to_create" toRequest:requestId]; // new Error("Missing required widget fields."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -517,7 +308,7 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
{
|
||||
if (!widgetType)
|
||||
{
|
||||
[self sendLocalisedError:@"widget_integration_unable_to_create" toEvent:eventData];
|
||||
[self sendLocalisedError:@"widget_integration_unable_to_create" toRequest:requestId];
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -536,7 +327,8 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
|
||||
[room sendStateEventOfType:kWidgetEventTypeString
|
||||
// TODO: Move to kWidgetMatrixEventTypeString ("m.widget") type but when?
|
||||
[room sendStateEventOfType:kWidgetModularEventTypeString
|
||||
content:widgetEventContent
|
||||
stateKey:widget_id
|
||||
success:^(NSString *eventId) {
|
||||
@@ -547,7 +339,7 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
[self sendNSObjectResponse:@{
|
||||
@"success": @(YES)
|
||||
}
|
||||
toEvent:eventData];
|
||||
toRequest:requestId];
|
||||
}
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
@@ -555,48 +347,52 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
typeof(self) self = weakSelf;
|
||||
if (self)
|
||||
{
|
||||
[self sendLocalisedError:@"widget_integration_failed_to_send_request" toEvent:eventData];
|
||||
[self sendLocalisedError:@"widget_integration_failed_to_send_request" toRequest:requestId];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)getWidgets:(NSDictionary*)eventData
|
||||
- (void)getWidgets:(NSString*)requestId data:(NSDictionary*)requestData
|
||||
{
|
||||
MXRoom *room = [self roomCheckWithEvent:eventData];
|
||||
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
|
||||
NSMutableArray<NSDictionary*> *widgetStateEvents = [NSMutableArray array];
|
||||
|
||||
if (room)
|
||||
{
|
||||
NSArray<Widget*> *widgets = [[WidgetManager sharedManager] widgetsInRoom:room];
|
||||
|
||||
NSMutableArray<NSDictionary*> *widgetStateEvents = [NSMutableArray arrayWithCapacity:widgets.count];
|
||||
|
||||
for (Widget *widget in widgets)
|
||||
{
|
||||
[widgetStateEvents addObject:widget.widgetEvent.JSONDictionary];
|
||||
}
|
||||
|
||||
[self sendNSObjectResponse:widgetStateEvents toEvent:eventData];
|
||||
}
|
||||
|
||||
// Add user widgets (not linked to a specific room)
|
||||
for (Widget *widget in [[WidgetManager sharedManager] userWidgets:mxSession])
|
||||
{
|
||||
[widgetStateEvents addObject:widget.widgetEvent.JSONDictionary];
|
||||
}
|
||||
|
||||
[self sendNSObjectResponse:widgetStateEvents toRequest:requestId];
|
||||
}
|
||||
|
||||
- (void)canSendEvent:(NSDictionary*)eventData
|
||||
- (void)canSendEvent:(NSString*)requestId data:(NSDictionary*)requestData
|
||||
{
|
||||
NSString *eventType;
|
||||
BOOL isState = NO;
|
||||
|
||||
MXRoom *room = [self roomCheckWithEvent:eventData];
|
||||
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
|
||||
|
||||
if (room)
|
||||
{
|
||||
if (room.state.membership != MXMembershipJoin)
|
||||
{
|
||||
[self sendLocalisedError:@"widget_integration_must_be_in_room" toEvent:eventData];
|
||||
[self sendLocalisedError:@"widget_integration_must_be_in_room" toRequest:requestId];
|
||||
return;
|
||||
}
|
||||
|
||||
MXJSONModelSetString(eventType, eventData[@"event_type"]);
|
||||
MXJSONModelSetBoolean(isState, eventData[@"is_state"]);
|
||||
MXJSONModelSetString(eventType, requestData[@"event_type"]);
|
||||
MXJSONModelSetBoolean(isState, requestData[@"is_state"]);
|
||||
|
||||
MXRoomPowerLevels *powerLevels = room.state.powerLevels;
|
||||
NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:mxSession.myUser.userId];
|
||||
@@ -614,48 +410,48 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
|
||||
if (canSend)
|
||||
{
|
||||
[self sendBoolResponse:YES toEvent:eventData];
|
||||
[self sendBoolResponse:YES toRequest:requestId];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self sendLocalisedError:@"widget_integration_no_permission_in_room" toEvent:eventData];
|
||||
[self sendLocalisedError:@"widget_integration_no_permission_in_room" toRequest:requestId];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)getMembershipState:(NSString*)userId eventData:(NSDictionary*)eventData
|
||||
- (void)getMembershipState:(NSString*)userId request:(NSString*)requestId data:(NSDictionary*)requestData
|
||||
{
|
||||
NSLog(@"[IntegrationManagerVC] membership_state of %@ in room %@ requested.", userId, roomId);
|
||||
|
||||
MXRoom *room = [self roomCheckWithEvent:eventData];
|
||||
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
|
||||
if (room)
|
||||
{
|
||||
MXRoomMember *member = [room.state memberWithUserId:userId];
|
||||
[self sendNSObjectResponse:member.originalEvent.content toEvent:eventData];
|
||||
[self sendNSObjectResponse:member.originalEvent.content toRequest:requestId];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)getJoinRules:(NSDictionary*)eventData
|
||||
- (void)getJoinRules:(NSString*)requestId data:(NSDictionary*)requestData
|
||||
{
|
||||
NSLog(@"[IntegrationManagerVC] join_rules of %@ requested.", roomId);
|
||||
|
||||
MXRoom *room = [self roomCheckWithEvent:eventData];
|
||||
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
|
||||
if (room)
|
||||
{
|
||||
MXEvent *event = [room.state stateEventsWithType:kMXEventTypeStringRoomJoinRules].lastObject;
|
||||
[self sendNSObjectResponse:event.JSONDictionary toEvent:eventData];
|
||||
[self sendNSObjectResponse:event.JSONDictionary toRequest:requestId];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setPlumbingState:(NSDictionary*)eventData
|
||||
- (void)setPlumbingState:(NSString*)requestId data:(NSDictionary*)requestData
|
||||
{
|
||||
NSLog(@"[IntegrationManagerVC] Received request to set plumbing state to status %@ in room %@.", eventData[@"status"], roomId);
|
||||
NSLog(@"[IntegrationManagerVC] Received request to set plumbing state to status %@ in room %@.", requestData[@"status"], roomId);
|
||||
|
||||
MXRoom *room = [self roomCheckWithEvent:eventData];
|
||||
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
|
||||
if (room)
|
||||
{
|
||||
NSString *status;
|
||||
MXJSONModelSetString(status, eventData[@"status"]);
|
||||
MXJSONModelSetString(status, requestData[@"status"]);
|
||||
|
||||
if (status)
|
||||
{
|
||||
@@ -674,7 +470,7 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
[self sendNSObjectResponse:@{
|
||||
@"success": @(YES)
|
||||
}
|
||||
toEvent:eventData];
|
||||
toRequest:requestId];
|
||||
}
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
@@ -682,7 +478,7 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
typeof(self) self = weakSelf;
|
||||
if (self)
|
||||
{
|
||||
[self sendLocalisedError:@"widget_integration_failed_to_send_request" toEvent:eventData];
|
||||
[self sendLocalisedError:@"widget_integration_failed_to_send_request" toRequest:requestId];
|
||||
}
|
||||
}];
|
||||
}
|
||||
@@ -693,11 +489,11 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
}
|
||||
}
|
||||
|
||||
- (void)getBotOptions:(NSString*)userId eventData:(NSDictionary*)eventData
|
||||
- (void)getBotOptions:(NSString*)userId request:(NSString*)requestId data:(NSDictionary*)requestData
|
||||
{
|
||||
NSLog(@"[IntegrationManagerVC] Received request to get options for bot %@ in room %@", userId, roomId);
|
||||
|
||||
MXRoom *room = [self roomCheckWithEvent:eventData];
|
||||
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
|
||||
if (room)
|
||||
{
|
||||
NSString *stateKey = [NSString stringWithFormat:@"_%@", userId];
|
||||
@@ -717,19 +513,19 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
}
|
||||
}
|
||||
|
||||
[self sendNSObjectResponse:botOptionsEvent.JSONDictionary toEvent:eventData];
|
||||
[self sendNSObjectResponse:botOptionsEvent.JSONDictionary toRequest:requestId];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setBotOptions:(NSString*)userId eventData:(NSDictionary*)eventData
|
||||
- (void)setBotOptions:(NSString*)userId request:(NSString*)requestId data:(NSDictionary*)requestData
|
||||
{
|
||||
NSLog(@"[IntegrationManagerVC] Received request to set options for bot %@ in room %@", userId, roomId);
|
||||
|
||||
MXRoom *room = [self roomCheckWithEvent:eventData];
|
||||
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
|
||||
if (room)
|
||||
{
|
||||
NSDictionary *content;
|
||||
MXJSONModelSetDictionary(content, eventData[@"content"]);
|
||||
MXJSONModelSetDictionary(content, requestData[@"content"]);
|
||||
|
||||
if (content)
|
||||
{
|
||||
@@ -748,7 +544,7 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
[self sendNSObjectResponse:@{
|
||||
@"success": @(YES)
|
||||
}
|
||||
toEvent:eventData];
|
||||
toRequest:requestId];
|
||||
}
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
@@ -756,7 +552,7 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
typeof(self) self = weakSelf;
|
||||
if (self)
|
||||
{
|
||||
[self sendLocalisedError:@"widget_integration_failed_to_send_request" toEvent:eventData];
|
||||
[self sendLocalisedError:@"widget_integration_failed_to_send_request" toRequest:requestId];
|
||||
}
|
||||
}];
|
||||
}
|
||||
@@ -767,15 +563,15 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setBotPower:(NSString*)userId eventData:(NSDictionary*)eventData
|
||||
- (void)setBotPower:(NSString*)userId request:(NSString*)requestId data:(NSDictionary*)requestData
|
||||
{
|
||||
NSLog(@"[IntegrationManagerVC] Received request to set power level to %@ for bot %@ in room %@.", eventData[@"level"], userId, roomId);
|
||||
NSLog(@"[IntegrationManagerVC] Received request to set power level to %@ for bot %@ in room %@.", requestData[@"level"], userId, roomId);
|
||||
|
||||
MXRoom *room = [self roomCheckWithEvent:eventData];
|
||||
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
|
||||
if (room)
|
||||
{
|
||||
NSInteger level = -1;
|
||||
MXJSONModelSetInteger(level, eventData[@"level"]);
|
||||
MXJSONModelSetInteger(level, requestData[@"level"]);
|
||||
|
||||
if (level >= 0)
|
||||
{
|
||||
@@ -789,7 +585,7 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
[self sendNSObjectResponse:@{
|
||||
@"success": @(YES)
|
||||
}
|
||||
toEvent:eventData];
|
||||
toRequest:requestId];
|
||||
}
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
@@ -797,25 +593,25 @@ NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@',
|
||||
typeof(self) self = weakSelf;
|
||||
if (self)
|
||||
{
|
||||
[self sendLocalisedError:@"widget_integration_failed_to_send_request" toEvent:eventData];
|
||||
[self sendLocalisedError:@"widget_integration_failed_to_send_request" toRequest:requestId];
|
||||
}
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"[IntegrationManagerVC] setBotPower. Power level must be positive integer.");
|
||||
[self sendLocalisedError:@"widget_integration_positive_power_level" toEvent:eventData];
|
||||
[self sendLocalisedError:@"widget_integration_positive_power_level" toRequest:requestId];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)getMembershipCount:(NSDictionary*)eventData
|
||||
- (void)getMembershipCount:(NSString*)requestId data:(NSDictionary*)requestData
|
||||
{
|
||||
MXRoom *room = [self roomCheckWithEvent:eventData];
|
||||
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
|
||||
if (room)
|
||||
{
|
||||
NSUInteger membershipCount = room.state.joinedMembers.count;
|
||||
[self sendIntegerResponse:membershipCount toEvent:eventData];
|
||||
[self sendIntegerResponse:membershipCount toRequest:requestId];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,12 +53,19 @@
|
||||
|
||||
MXRoom *room = [mxSession roomWithRoomId:roomId];
|
||||
|
||||
NSArray<Widget*> *widgets = [[WidgetManager sharedManager] widgetsNotOfTypes:@[kWidgetTypeJitsi]
|
||||
NSArray<Widget*> *roomWidgets = [[WidgetManager sharedManager] widgetsNotOfTypes:@[kWidgetTypeJitsi]
|
||||
inRoom:room];
|
||||
|
||||
NSArray<Widget*> *userWidgets = [[WidgetManager sharedManager] userWidgets:room.mxSession];
|
||||
|
||||
NSMutableArray<Widget*> *widgets = [NSMutableArray array];
|
||||
[widgets addObjectsFromArray:roomWidgets];
|
||||
[widgets addObjectsFromArray:userWidgets];
|
||||
|
||||
// List widgets
|
||||
for (Widget *widget in widgets)
|
||||
{
|
||||
alertAction = [UIAlertAction actionWithTitle:widget.name
|
||||
alertAction = [UIAlertAction actionWithTitle:widget.name ? widget.name : widget.type
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * _Nonnull action)
|
||||
{
|
||||
@@ -68,8 +75,12 @@
|
||||
// Display the widget
|
||||
[widget widgetUrl:^(NSString * _Nonnull widgetUrl) {
|
||||
|
||||
WidgetViewController *widgetVC = [[WidgetViewController alloc] initWithUrl:widgetUrl forWidget:widget];
|
||||
[mxkViewController.navigationController pushViewController:widgetVC animated:YES];
|
||||
WidgetViewController *widgetVC = [[WidgetViewController alloc] initWithUrl:widgetUrl forWidget:widget];
|
||||
|
||||
MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:mxSession];
|
||||
widgetVC.roomDataSource = [roomDataSourceManager roomDataSourceForRoom:roomId create:NO];
|
||||
|
||||
[mxkViewController.navigationController pushViewController:widgetVC animated:YES];
|
||||
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
|
||||
|
||||
@@ -17,12 +17,22 @@
|
||||
#import "WebViewViewController.h"
|
||||
|
||||
#import "WidgetManager.h"
|
||||
#import "MatrixKit/MatrixKit.h"
|
||||
|
||||
/**
|
||||
`WidgetViewController` displays widget within a webview.
|
||||
|
||||
It also exposes a generic pipe, the postMessage API, to communicate with the
|
||||
content within the webview, ie the widget (matrix app).
|
||||
*/
|
||||
@interface WidgetViewController : WebViewViewController
|
||||
|
||||
/**
|
||||
The room data source.
|
||||
Required if the widget needs to post messages.
|
||||
*/
|
||||
@property (nonatomic) MXKRoomDataSource *roomDataSource;
|
||||
|
||||
/**
|
||||
Init 'WidgetViewController' instance with a widget.
|
||||
|
||||
@@ -31,4 +41,65 @@
|
||||
*/
|
||||
- (instancetype)initWithUrl:(NSString*)widgetUrl forWidget:(Widget*)widget;
|
||||
|
||||
/**
|
||||
Display an alert over this controller.
|
||||
|
||||
@param error the error to display.
|
||||
*/
|
||||
- (void)showErrorAsAlert:(NSError*)error;
|
||||
|
||||
|
||||
#pragma mark - postMessage API
|
||||
|
||||
/**
|
||||
Callback called when the widget make a postMessage API request.
|
||||
|
||||
This method can be overidden to implement a specific API between the matrix client
|
||||
and widget.
|
||||
|
||||
@param requestId the id of the widget request.
|
||||
@param requestData the request data.
|
||||
*/
|
||||
- (void)onPostMessageRequest:(NSString*)requestId data:(NSDictionary*)requestData;
|
||||
|
||||
/**
|
||||
Send a boolean response to a request from the widget.
|
||||
|
||||
@param response the response to send.
|
||||
@param requestId the id of the widget request.
|
||||
*/
|
||||
- (void)sendBoolResponse:(BOOL)response toRequest:(NSString*)requestId;
|
||||
|
||||
/**
|
||||
Send an integer response to a request from the widget.
|
||||
|
||||
@param response the response to send.
|
||||
@param requestId the id of the widget request.
|
||||
*/
|
||||
- (void)sendIntegerResponse:(NSUInteger)response toRequest:(NSString*)requestId;
|
||||
|
||||
/**
|
||||
Send a serialiable object response to a request the widget.
|
||||
|
||||
@param response the response to send.
|
||||
@param requestId the id of the widget request.
|
||||
*/
|
||||
- (void)sendNSObjectResponse:(NSObject*)response toRequest:(NSString*)requestId;
|
||||
|
||||
/**
|
||||
Send a message error to a request from the widget.
|
||||
|
||||
@param message the error message.
|
||||
@param requestId the id of the widget request.
|
||||
*/
|
||||
- (void)sendError:(NSString*)message toRequest:(NSString*)requestId;
|
||||
|
||||
/**
|
||||
Send a localised message error to a request from the widget.
|
||||
|
||||
@param errorKey the string id of the message error.
|
||||
@param requestId the id of the widget request.
|
||||
*/
|
||||
- (void)sendLocalisedError:(NSString*)errorKey toRequest:(NSString*)requestId;
|
||||
|
||||
@end
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
#import "WidgetViewController.h"
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse('%@', %@);";
|
||||
|
||||
@interface WidgetViewController ()
|
||||
{
|
||||
Widget *widget;
|
||||
@@ -42,7 +46,239 @@
|
||||
webView.scalesPageToFit = NO;
|
||||
webView.scrollView.bounces = NO;
|
||||
|
||||
self.navigationItem.title = widget.name;
|
||||
// Disable opacity so that the webview background uses the current interface theme
|
||||
webView.opaque = NO;
|
||||
|
||||
if (widget)
|
||||
{
|
||||
self.navigationItem.title = widget.name ? widget.name : widget.type;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)showErrorAsAlert:(NSError*)error
|
||||
{
|
||||
NSString *title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey];
|
||||
NSString *msg = [error.userInfo valueForKey:NSLocalizedDescriptionKey];
|
||||
if (!title)
|
||||
{
|
||||
if (msg)
|
||||
{
|
||||
title = msg;
|
||||
msg = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
title = [NSBundle mxk_localizedStringForKey:@"error"];
|
||||
}
|
||||
}
|
||||
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
typeof(self) self = weakSelf;
|
||||
|
||||
if (self)
|
||||
{
|
||||
// Leave this widget VC
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - UIWebViewDelegate
|
||||
|
||||
-(void)webViewDidFinishLoad:(UIWebView *)theWebView
|
||||
{
|
||||
[self enableDebug];
|
||||
|
||||
// Setup js code
|
||||
NSString *path = [[NSBundle mainBundle] pathForResource:@"postMessageAPI" ofType:@"js"];
|
||||
NSString *js = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
|
||||
[webView stringByEvaluatingJavaScriptFromString:js];
|
||||
|
||||
[self stopActivityIndicator];
|
||||
|
||||
// Check connectivity
|
||||
if ([AppDelegate theDelegate].isOffline)
|
||||
{
|
||||
// The web page may be in the cache, so its loading will be successful
|
||||
// but we cannot go further, it often leads to a blank screen.
|
||||
// So, display an error so that the user can escape.
|
||||
NSError *error = [NSError errorWithDomain:NSURLErrorDomain
|
||||
code:NSURLErrorNotConnectedToInternet
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey : NSLocalizedStringFromTable(@"network_offline_prompt", @"Vector", nil)
|
||||
}];
|
||||
[self showErrorAsAlert:error];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
|
||||
{
|
||||
NSString *urlString = [[request URL] absoluteString];
|
||||
|
||||
if ([urlString hasPrefix:@"js:"])
|
||||
{
|
||||
// Listen only to scheme of the JS-UIWebView bridge
|
||||
NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
|
||||
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSError *error;
|
||||
NSDictionary *parameters = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers
|
||||
error:&error];
|
||||
if (!error)
|
||||
{
|
||||
// Retrieve the js event payload data
|
||||
NSDictionary *eventData;
|
||||
MXJSONModelSetDictionary(eventData, parameters[@"event.data"]);
|
||||
|
||||
NSString *requestId;
|
||||
MXJSONModelSetString(requestId, eventData[@"_id"]);
|
||||
|
||||
if (requestId)
|
||||
{
|
||||
[self onPostMessageRequest:requestId data:eventData];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"[WidgetVC] shouldStartLoadWithRequest: ERROR: Missing request id in postMessage API %@", parameters);
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (navigationType == UIWebViewNavigationTypeLinkClicked )
|
||||
{
|
||||
// Open links outside the app
|
||||
[[UIApplication sharedApplication] openURL:[request URL]];
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
|
||||
{
|
||||
// Filter out the users's scalar token
|
||||
NSString *errorDescription = error.description;
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"scalar_token=\\w*"
|
||||
options:NSRegularExpressionCaseInsensitive error:nil];
|
||||
errorDescription = [regex stringByReplacingMatchesInString:errorDescription
|
||||
options:0
|
||||
range:NSMakeRange(0, errorDescription.length)
|
||||
withTemplate:@"scalar_token=..."];
|
||||
|
||||
NSLog(@"[WidgetVC] didFailLoadWithError: %@", errorDescription);
|
||||
|
||||
[self stopActivityIndicator];
|
||||
[self showErrorAsAlert:error];
|
||||
}
|
||||
|
||||
#pragma mark - postMessage API
|
||||
|
||||
- (void)onPostMessageRequest:(NSString*)requestId data:(NSDictionary*)requestData
|
||||
{
|
||||
NSString *action;
|
||||
MXJSONModelSetString(action, requestData[@"action"]);
|
||||
|
||||
if ([@"m.sticker" isEqualToString:action])
|
||||
{
|
||||
// Extract the sticker event content and send it as is
|
||||
|
||||
// The key should be "data" according to https://docs.google.com/document/d/1uPF7XWY_dXTKVKV7jZQ2KmsI19wn9-kFRgQ1tFQP7wQ/edit?usp=sharing
|
||||
// TODO: Fix it once spec is finalised
|
||||
NSDictionary *widgetData;
|
||||
NSDictionary *stickerContent;
|
||||
MXJSONModelSetDictionary(widgetData, requestData[@"widgetData"]);
|
||||
if (widgetData)
|
||||
{
|
||||
MXJSONModelSetDictionary(stickerContent, widgetData[@"content"]);
|
||||
}
|
||||
|
||||
if (stickerContent)
|
||||
{
|
||||
// Let the data source manage the sending cycle
|
||||
[_roomDataSource sendEventOfType:kMXEventTypeStringSticker content:stickerContent success:nil failure:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"[WidgetVC] onPostMessageRequest: ERROR: Invalid content for m.sticker: %@", requestData);
|
||||
}
|
||||
|
||||
// Consider we are done with the sticker picker widget
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendBoolResponse:(BOOL)response toRequest:(NSString*)requestId
|
||||
{
|
||||
// Convert BOOL to "true" or "false"
|
||||
NSString *js = [NSString stringWithFormat:kJavascriptSendResponseToPostMessageAPI,
|
||||
requestId,
|
||||
response ? @"true" : @"false"];
|
||||
|
||||
[webView stringByEvaluatingJavaScriptFromString:js];
|
||||
}
|
||||
|
||||
- (void)sendIntegerResponse:(NSUInteger)response toRequest:(NSString*)requestId
|
||||
{
|
||||
NSString *js = [NSString stringWithFormat:kJavascriptSendResponseToPostMessageAPI,
|
||||
requestId,
|
||||
@(response)];
|
||||
|
||||
[webView stringByEvaluatingJavaScriptFromString:js];
|
||||
}
|
||||
|
||||
- (void)sendNSObjectResponse:(NSObject*)response toRequest:(NSString*)requestId
|
||||
{
|
||||
NSString *jsString;
|
||||
|
||||
if (response)
|
||||
{
|
||||
// Convert response into a JS object through a JSON string
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:response
|
||||
options:0
|
||||
error:0];
|
||||
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
|
||||
jsString = [NSString stringWithFormat:@"JSON.parse('%@')", jsonString];
|
||||
}
|
||||
else
|
||||
{
|
||||
jsString = @"null";
|
||||
}
|
||||
|
||||
NSString *js = [NSString stringWithFormat:kJavascriptSendResponseToPostMessageAPI,
|
||||
requestId,
|
||||
jsString];
|
||||
|
||||
[webView stringByEvaluatingJavaScriptFromString:js];
|
||||
}
|
||||
|
||||
- (void)sendError:(NSString*)message toRequest:(NSString*)requestId
|
||||
{
|
||||
NSLog(@"[WidgetVC] sendError: Action %@ failed with message: %@", requestId, message);
|
||||
|
||||
// TODO: JS has an additional optional parameter: nestedError
|
||||
[self sendNSObjectResponse:@{
|
||||
@"error": @{
|
||||
@"message": message
|
||||
}
|
||||
}
|
||||
toRequest:requestId];
|
||||
}
|
||||
|
||||
- (void)sendLocalisedError:(NSString*)errorKey toRequest:(NSString*)requestId
|
||||
{
|
||||
[self sendError:NSLocalizedStringFromTable(errorKey, @"Vector", nil) toRequest:requestId];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.6.15</string>
|
||||
<string>0.6.16</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSExtension</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.6.15</string>
|
||||
<string>0.6.16</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSExtension</key>
|
||||
|
||||
Reference in New Issue
Block a user