diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index fa30fbcc7..3bd423c32 100755 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -64,6 +64,8 @@ 327382C41F276AED00356143 /* Vector.strings in Resources */ = {isa = PBXBuildFile; fileRef = 327382C01F276AED00356143 /* Vector.strings */; }; 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 */; }; 32AE61E41F0A971B007255F4 /* RoomMembershipBubbleCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 32AE61E21F0A971B007255F4 /* RoomMembershipBubbleCell.m */; }; 32AE61E51F0A971B007255F4 /* RoomMembershipBubbleCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32AE61E31F0A971B007255F4 /* RoomMembershipBubbleCell.xib */; }; 32AE61E91F0CE099007255F4 /* RoomMembershipWithPaginationTitleBubbleCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 32AE61E71F0CE099007255F4 /* RoomMembershipWithPaginationTitleBubbleCell.m */; }; @@ -73,6 +75,10 @@ 32AE61F41F0D2183007255F4 /* Vector.strings in Resources */ = {isa = PBXBuildFile; fileRef = 32AE61F01F0D2183007255F4 /* Vector.strings */; }; 32D392181EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D392161EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.m */; }; 32D392191EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32D392171EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.xib */; }; + 32E84FA11F6BD32700CA0B89 /* apps-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 32E84F9E1F6BD32700CA0B89 /* apps-icon.png */; }; + 32E84FA21F6BD32700CA0B89 /* apps-icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 32E84F9F1F6BD32700CA0B89 /* apps-icon@2x.png */; }; + 32E84FA31F6BD32700CA0B89 /* apps-icon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 32E84FA01F6BD32700CA0B89 /* apps-icon@3x.png */; }; + 32F3AE1A1F6FF4E600F0F004 /* WidgetViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F3AE191F6FF4E600F0F004 /* WidgetViewController.m */; }; 32FD0A3D1EB0CD9B0072B066 /* BugReportViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FD0A3B1EB0CD9B0072B066 /* BugReportViewController.m */; }; 32FD0A3E1EB0CD9B0072B066 /* BugReportViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32FD0A3C1EB0CD9B0072B066 /* BugReportViewController.xib */; }; 714F6391AC0AA86C0AEB3F43 /* libPods-SiriIntents.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5666C1236223F54D4C635C54 /* libPods-SiriIntents.a */; }; @@ -495,7 +501,9 @@ F0B4CBAC1F41F090008E99C5 /* EncryptionInfoView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F0B4CBAB1F41F090008E99C5 /* EncryptionInfoView.xib */; }; F0B4CBB11F4215E3008E99C5 /* EventDetailsView.m in Sources */ = {isa = PBXBuildFile; fileRef = F0B4CBAF1F4215E3008E99C5 /* EventDetailsView.m */; }; F0B4CBB21F4215E3008E99C5 /* EventDetailsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F0B4CBB01F4215E3008E99C5 /* EventDetailsView.xib */; }; + F0B5430A1F70FF8F00D29293 /* back_icon@4x.png in Resources */ = {isa = PBXBuildFile; fileRef = F0B543091F70FF8F00D29293 /* back_icon@4x.png */; }; F0B7A8B11F475783006E27D2 /* RoomsListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F0B7A8AF1F4756A5006E27D2 /* RoomsListViewController.xib */; }; + F0D2ADA11F6AA5FD00A7097D /* MXRoomSummary+Riot.m in Sources */ = {isa = PBXBuildFile; fileRef = F0D2ADA01F6AA5FD00A7097D /* MXRoomSummary+Riot.m */; }; F0D869EB1EC455A100BB0A2B /* create_direct_chat.png in Resources */ = {isa = PBXBuildFile; fileRef = F0D869E81EC455A100BB0A2B /* create_direct_chat.png */; }; F0D869EC1EC455A100BB0A2B /* create_direct_chat@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F0D869E91EC455A100BB0A2B /* create_direct_chat@2x.png */; }; F0D869ED1EC455A100BB0A2B /* create_direct_chat@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = F0D869EA1EC455A100BB0A2B /* create_direct_chat@3x.png */; }; @@ -642,6 +650,9 @@ 327382C11F276AED00356143 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Vector.strings; sourceTree = ""; }; 32918EA61F473BDB0076CA16 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = Localizable.strings; sourceTree = ""; }; 32918EA81F473BDB0076CA16 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = Vector.strings; sourceTree = ""; }; + 32935CAF1F6056FD006888C8 /* IntegrationManagerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntegrationManagerViewController.h; sourceTree = ""; }; + 32935CB01F6056FD006888C8 /* IntegrationManagerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IntegrationManagerViewController.m; sourceTree = ""; }; + 32935CB31F628BCE006888C8 /* IntegrationManager.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = IntegrationManager.js; sourceTree = ""; }; 32AE61E11F0A971B007255F4 /* RoomMembershipBubbleCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RoomMembershipBubbleCell.h; sourceTree = ""; }; 32AE61E21F0A971B007255F4 /* RoomMembershipBubbleCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RoomMembershipBubbleCell.m; sourceTree = ""; }; 32AE61E31F0A971B007255F4 /* RoomMembershipBubbleCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RoomMembershipBubbleCell.xib; sourceTree = ""; }; @@ -654,6 +665,11 @@ 32D392151EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectoryServerDetailTableViewCell.h; sourceTree = ""; }; 32D392161EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DirectoryServerDetailTableViewCell.m; sourceTree = ""; }; 32D392171EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DirectoryServerDetailTableViewCell.xib; sourceTree = ""; }; + 32E84F9E1F6BD32700CA0B89 /* apps-icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "apps-icon.png"; sourceTree = ""; }; + 32E84F9F1F6BD32700CA0B89 /* apps-icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "apps-icon@2x.png"; sourceTree = ""; }; + 32E84FA01F6BD32700CA0B89 /* apps-icon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "apps-icon@3x.png"; sourceTree = ""; }; + 32F3AE181F6FF4E600F0F004 /* WidgetViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WidgetViewController.h; sourceTree = ""; }; + 32F3AE191F6FF4E600F0F004 /* WidgetViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WidgetViewController.m; sourceTree = ""; }; 32FD0A3A1EB0CD9B0072B066 /* BugReportViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BugReportViewController.h; sourceTree = ""; }; 32FD0A3B1EB0CD9B0072B066 /* BugReportViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugReportViewController.m; sourceTree = ""; }; 32FD0A3C1EB0CD9B0072B066 /* BugReportViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BugReportViewController.xib; sourceTree = ""; }; @@ -1211,7 +1227,10 @@ F0B4CBAE1F4215E3008E99C5 /* EventDetailsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventDetailsView.h; sourceTree = ""; }; F0B4CBAF1F4215E3008E99C5 /* EventDetailsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EventDetailsView.m; sourceTree = ""; }; F0B4CBB01F4215E3008E99C5 /* EventDetailsView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EventDetailsView.xib; sourceTree = ""; }; + F0B543091F70FF8F00D29293 /* back_icon@4x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "back_icon@4x.png"; sourceTree = ""; }; F0B7A8AF1F4756A5006E27D2 /* RoomsListViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RoomsListViewController.xib; sourceTree = ""; }; + F0D2AD9F1F6AA5FD00A7097D /* MXRoomSummary+Riot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MXRoomSummary+Riot.h"; sourceTree = ""; }; + F0D2ADA01F6AA5FD00A7097D /* MXRoomSummary+Riot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MXRoomSummary+Riot.m"; sourceTree = ""; }; F0D869E81EC455A100BB0A2B /* create_direct_chat.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = create_direct_chat.png; sourceTree = ""; }; F0D869E91EC455A100BB0A2B /* create_direct_chat@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "create_direct_chat@2x.png"; sourceTree = ""; }; F0D869EA1EC455A100BB0A2B /* create_direct_chat@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "create_direct_chat@3x.png"; sourceTree = ""; }; @@ -1356,6 +1375,10 @@ 3233F72D1F31F47E006ACA81 /* Widgets */ = { isa = PBXGroup; children = ( + 32F3AE181F6FF4E600F0F004 /* WidgetViewController.h */, + 32F3AE191F6FF4E600F0F004 /* WidgetViewController.m */, + 32935CAF1F6056FD006888C8 /* IntegrationManagerViewController.h */, + 32935CB01F6056FD006888C8 /* IntegrationManagerViewController.m */, 3233F72E1F31F4BF006ACA81 /* JitsiViewController.h */, 3233F72F1F31F4BF006ACA81 /* JitsiViewController.m */, 3233F7301F31F4BF006ACA81 /* JitsiViewController.xib */, @@ -1421,6 +1444,14 @@ path = ru.lproj; sourceTree = ""; }; + 32935CB21F628B98006888C8 /* js */ = { + isa = PBXGroup; + children = ( + 32935CB31F628BCE006888C8 /* IntegrationManager.js */, + ); + path = js; + sourceTree = ""; + }; 32AE61EB1F0D2183007255F4 /* nl.lproj */ = { isa = PBXGroup; children = ( @@ -1533,8 +1564,9 @@ 327382BB1F276AED00356143 /* en.lproj */, 327382AE1F276AD200356143 /* fr.lproj */, 32AE61EB1F0D2183007255F4 /* nl.lproj */, - F083BB151E7009EC00A9B29C /* Images */, F083BBDA1E7009EC00A9B29C /* Sounds */, + F083BB151E7009EC00A9B29C /* Images */, + 32935CB21F628B98006888C8 /* js */, ); path = Assets; sourceTree = ""; @@ -1542,6 +1574,9 @@ F083BB151E7009EC00A9B29C /* Images */ = { isa = PBXGroup; children = ( + 32E84F9E1F6BD32700CA0B89 /* apps-icon.png */, + 32E84F9F1F6BD32700CA0B89 /* apps-icon@2x.png */, + 32E84FA01F6BD32700CA0B89 /* apps-icon@3x.png */, F0A4A1641EF7CB66003630DB /* members_list_icon.png */, F0A4A1651EF7CB66003630DB /* members_list_icon@2x.png */, F0A4A1661EF7CB66003630DB /* members_list_icon@3x.png */, @@ -1592,6 +1627,7 @@ F083BB211E7009EC00A9B29C /* back_icon.png */, F083BB221E7009EC00A9B29C /* back_icon@2x.png */, F083BB231E7009EC00A9B29C /* back_icon@3x.png */, + F0B543091F70FF8F00D29293 /* back_icon@4x.png */, F083BB241E7009EC00A9B29C /* bubbles_bg_landscape.png */, F083BB251E7009EC00A9B29C /* bubbles_bg_landscape@2x.png */, F083BB261E7009EC00A9B29C /* bubbles_bg_landscape@3x.png */, @@ -1797,6 +1833,8 @@ F083BBE61E7009EC00A9B29C /* MXKRoomBubbleTableViewCell+Riot.m */, F083BBE71E7009EC00A9B29C /* MXRoom+Riot.h */, F083BBE81E7009EC00A9B29C /* MXRoom+Riot.m */, + F0D2AD9F1F6AA5FD00A7097D /* MXRoomSummary+Riot.h */, + F0D2ADA01F6AA5FD00A7097D /* MXRoomSummary+Riot.m */, F083BBE91E7009EC00A9B29C /* UINavigationController+Riot.h */, F083BBEA1E7009EC00A9B29C /* UINavigationController+Riot.m */, F083BBEB1E7009EC00A9B29C /* UIViewController+RiotSearch.h */, @@ -2560,6 +2598,7 @@ F083BE1D1E7009ED00A9B29C /* RoomMemberDetailsViewController.xib in Resources */, F083BD5B1E7009ED00A9B29C /* chevron@3x.png in Resources */, F083BD8A1E7009ED00A9B29C /* file_music_icon@2x.png in Resources */, + 32E84FA31F6BD32700CA0B89 /* apps-icon@3x.png in Resources */, F083BE451E7009ED00A9B29C /* RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.xib in Resources */, F083BD611E7009ED00A9B29C /* details_icon@3x.png in Resources */, F083BD9D1E7009ED00A9B29C /* main_alias_icon@3x.png in Resources */, @@ -2783,7 +2822,9 @@ F083BD591E7009ED00A9B29C /* chevron.png in Resources */, F0D869EC1EC455A100BB0A2B /* create_direct_chat@2x.png in Resources */, F083BDE11E7009ED00A9B29C /* video_icon@2x.png in Resources */, + 32E84FA21F6BD32700CA0B89 /* apps-icon@2x.png in Resources */, F083BDB51E7009ED00A9B29C /* priorityLow@3x.png in Resources */, + 32E84FA11F6BD32700CA0B89 /* apps-icon.png in Resources */, F083BE891E7009ED00A9B29C /* RoomMemberTitleView.xib in Resources */, F083BE3D1E7009ED00A9B29C /* RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.xib in Resources */, F0E05A431EA0F9EB004B83FB /* tab_rooms_selected@2x.png in Resources */, @@ -2844,6 +2885,7 @@ F083BE771E7009ED00A9B29C /* RoomOutgoingTextMsgWithPaginationTitleBubbleCell.xib in Resources */, F083BDA71E7009ED00A9B29C /* notificationsOff.png in Resources */, F06CDD6A1EF01E3900870B75 /* RoomEmptyBubbleCell.xib in Resources */, + F0B5430A1F70FF8F00D29293 /* back_icon@4x.png in Resources */, F083BDBD1E7009ED00A9B29C /* riot_icon@2x.png in Resources */, F083BDDE1E7009ED00A9B29C /* upload_icon@2x.png in Resources */, F083BE8B1E7009ED00A9B29C /* ExpandedRoomTitleView.xib in Resources */, @@ -2858,6 +2900,7 @@ 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 */, F083BD6A1E7009ED00A9B29C /* directChatOn@3x.png in Resources */, @@ -3202,6 +3245,8 @@ F083BE6E1E7009ED00A9B29C /* RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m in Sources */, 3233F7311F31F4BF006ACA81 /* JitsiViewController.m in Sources */, F083BE2A1E7009ED00A9B29C /* UsersDevicesViewController.m in Sources */, + 32F3AE1A1F6FF4E600F0F004 /* WidgetViewController.m in Sources */, + F0D2ADA11F6AA5FD00A7097D /* MXRoomSummary+Riot.m in Sources */, F083BE2D1E7009ED00A9B29C /* ForgotPasswordInputsView.m in Sources */, F083BE7E1E7009ED00A9B29C /* InviteRecentTableViewCell.m in Sources */, F083BE3C1E7009ED00A9B29C /* RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.m in Sources */, @@ -3269,6 +3314,7 @@ F083BE391E7009ED00A9B29C /* RoomEncryptedDataBubbleCell.m in Sources */, F083BDF01E7009ED00A9B29C /* UIViewController+RiotSearch.m in Sources */, F083BDF91E7009ED00A9B29C /* RoomEmailInvitation.m in Sources */, + 32935CB11F6056FD006888C8 /* IntegrationManagerViewController.m in Sources */, F083BE341E7009ED00A9B29C /* EncryptionInfoView.m in Sources */, F083BE641E7009ED00A9B29C /* RoomIncomingTextMsgWithoutSenderNameBubbleCell.m in Sources */, F075BED61EBB169C00A7B68A /* RoomCollectionViewCell.m in Sources */, diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 736fe8871..cb193e5bd 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -37,7 +37,6 @@ #import "MatrixSDK/MatrixSDK.h" #import "Tools.h" -#import "MXRoom+Riot.h" #import "WidgetManager.h" #import "AFNetworkReachabilityManager.h" @@ -1770,6 +1769,9 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // Remove inApp notifications toggle change MXKAccount *account = notif.object; [account removeObserver:self forKeyPath:@"enableInAppNotifications"]; + + // Clear Modular data + [[WidgetManager sharedManager] deleteDataForUser:account.mxCredentials.userId]; // Logout the app when there is no available account if (![MXKAccountManager sharedManager].accounts.count) @@ -2808,7 +2810,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN [_jitsiViewController dismissViewControllerAnimated:YES completion:^{ MXRoom *room = [_jitsiViewController.widget.mxSession roomWithRoomId:_jitsiViewController.widget.roomId]; - NSString *btnTitle = [NSString stringWithFormat:NSLocalizedStringFromTable(@"active_call_details", @"Vector", nil), room.riotDisplayname]; + NSString *btnTitle = [NSString stringWithFormat:NSLocalizedStringFromTable(@"active_call_details", @"Vector", nil), room.summary.displayname]; [self addCallStatusBar:btnTitle]; if (completion) diff --git a/Riot/Assets/Images/apps-icon.png b/Riot/Assets/Images/apps-icon.png new file mode 100644 index 000000000..a2751b1eb Binary files /dev/null and b/Riot/Assets/Images/apps-icon.png differ diff --git a/Riot/Assets/Images/apps-icon@2x.png b/Riot/Assets/Images/apps-icon@2x.png new file mode 100644 index 000000000..6061fa965 Binary files /dev/null and b/Riot/Assets/Images/apps-icon@2x.png differ diff --git a/Riot/Assets/Images/apps-icon@3x.png b/Riot/Assets/Images/apps-icon@3x.png new file mode 100644 index 000000000..cbad4c960 Binary files /dev/null and b/Riot/Assets/Images/apps-icon@3x.png differ diff --git a/Riot/Assets/Images/back_icon.png b/Riot/Assets/Images/back_icon.png index f7f8bdd24..e922a69d1 100644 Binary files a/Riot/Assets/Images/back_icon.png and b/Riot/Assets/Images/back_icon.png differ diff --git a/Riot/Assets/Images/back_icon@2x.png b/Riot/Assets/Images/back_icon@2x.png index 9ec1e9683..e996da664 100644 Binary files a/Riot/Assets/Images/back_icon@2x.png and b/Riot/Assets/Images/back_icon@2x.png differ diff --git a/Riot/Assets/Images/back_icon@3x.png b/Riot/Assets/Images/back_icon@3x.png index 83a500f10..32cdf4b60 100644 Binary files a/Riot/Assets/Images/back_icon@3x.png and b/Riot/Assets/Images/back_icon@3x.png differ diff --git a/Riot/Assets/Images/back_icon@4x.png b/Riot/Assets/Images/back_icon@4x.png new file mode 100644 index 000000000..7e4d8d484 Binary files /dev/null and b/Riot/Assets/Images/back_icon@4x.png differ diff --git a/Riot/Assets/Images/e2e_unencrypted.png b/Riot/Assets/Images/e2e_unencrypted.png old mode 100755 new mode 100644 index c7f1a4085..622d45a55 Binary files a/Riot/Assets/Images/e2e_unencrypted.png and b/Riot/Assets/Images/e2e_unencrypted.png differ diff --git a/Riot/Assets/Images/e2e_unencrypted@2x.png b/Riot/Assets/Images/e2e_unencrypted@2x.png old mode 100755 new mode 100644 index e6f83cfe2..896f0d101 Binary files a/Riot/Assets/Images/e2e_unencrypted@2x.png and b/Riot/Assets/Images/e2e_unencrypted@2x.png differ diff --git a/Riot/Assets/Images/e2e_unencrypted@3x.png b/Riot/Assets/Images/e2e_unencrypted@3x.png old mode 100755 new mode 100644 index 4366ed4fe..8b489fb2d Binary files a/Riot/Assets/Images/e2e_unencrypted@3x.png and b/Riot/Assets/Images/e2e_unencrypted@3x.png differ diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 4d6cc4b1e..88fb73a71 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -354,6 +354,7 @@ "settings_labs_e2e_encryption" = "End-to-End Encryption"; "settings_labs_e2e_encryption_prompt_message" = "To finish setting up encryption you must log in again."; +"settings_labs_matrix_apps" = "Matrix Apps"; "settings_labs_create_conference_with_jitsi" = "Create conference calls with jitsi"; "settings_version" = "Version %@"; @@ -514,3 +515,15 @@ "widget_no_power_to_manage" = "You need permission to manage widgets in this room"; "widget_creation_failure" = "Widget creation has failed"; +// Widget Integration Manager +"widget_integration_need_to_be_able_to_invite" = "You need to be able to invite users to do that."; +"widget_integration_unable_to_create" = "Unable to create widget."; +"widget_integration_failed_to_send_request" = "Failed to send request."; +"widget_integration_room_not_recognised" = "This room is not recognised."; +"widget_integration_positive_power_level" = "Power level must be positive integer."; +"widget_integration_must_be_in_room" = "You are not in this room."; +"widget_integration_no_permission_in_room" = "You do not have permission to do that in this room."; +"widget_integration_missing_room_id" = "Missing room_id in request."; +"widget_integration_missing_user_id" = "Missing user_id in request."; +"widget_integration_room_not_visible" = "Room %@ is not visible."; + diff --git a/Riot/Assets/js/IntegrationManager.js b/Riot/Assets/js/IntegrationManager.js new file mode 100644 index 000000000..5dc414677 --- /dev/null +++ b/Riot/Assets/js/IntegrationManager.js @@ -0,0 +1,67 @@ +/* + 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. + */ + +window.riotIOS = {}; + +// Generic JS -> ObjC bridge +window.riotIOS.sendObjectMessageToObjC = function(parameters) { + var iframe = document.createElement('iframe'); + iframe.setAttribute('src', 'js:' + JSON.stringify(parameters)); + + document.documentElement.appendChild(iframe); + iframe.parentNode.removeChild(iframe); + iframe = null; +}; + +window.riotIOS.events = {}; + +// Listen to messages posted by Modular +window.riotIOS.onMessage = function(event) { + + // Do not SPAM ObjC with event already managed + if (riotIOS.events[event.data._id]) { + return; + } + + if (!event.origin) { // stupid chrome + event.origin = event.originalEvent.origin; + } + + // Keep this event for future usage + riotIOS.events[event.data._id] = event; + + riotIOS.sendObjectMessageToObjC({ + 'event.data': event.data, + }); +}; +window.addEventListener('message', riotIOS.onMessage, false); + + +// ObjC -> Modular JS bridge +window.riotIOS.sendResponse = function(eventId, res) { + + // Retrieve the correspong JS event + var event = riotIOS.events[eventId]; + + console.log("sendResponse to " + event.data.action + " for "+ eventId + ": " + JSON.stringify(res)); + + var data = JSON.parse(JSON.stringify(event.data)); + data.response = res; + event.source.postMessage(data, event.origin); + + // Mark this event as handled + riotIOS.events[eventId] = true; +} diff --git a/Riot/Base.lproj/Main.storyboard b/Riot/Base.lproj/Main.storyboard index e49c5d470..0129f014a 100644 --- a/Riot/Base.lproj/Main.storyboard +++ b/Riot/Base.lproj/Main.storyboard @@ -1,11 +1,12 @@ - + + @@ -36,7 +37,14 @@ - + + + + + + + + @@ -564,6 +572,7 @@ + @@ -577,10 +586,10 @@ - - - - + + + + diff --git a/Riot/Categories/MXRoom+Riot.h b/Riot/Categories/MXRoom+Riot.h index 107c5cd20..e6e4be5a0 100644 --- a/Riot/Categories/MXRoom+Riot.h +++ b/Riot/Categories/MXRoom+Riot.h @@ -22,11 +22,6 @@ */ @interface MXRoom (Riot) -/** - The Riot displayname of the room - */ -@property(nonatomic, readonly) NSString* riotDisplayname; - /** Tell whether all the notifications are disabled for the room. */ @@ -47,17 +42,6 @@ */ @property (nonatomic) id notificationCenterDidUpdateObserver; -/** - Set the room avatar in the dedicated MXKImageView. - The vector style implies to use in order : - 1 - the default avatar if there is one - 2 - the member avatar for < 3 members rooms - 3 - the first later of the room name. - - @param mxkImageView the destinated MXKImageView. - */ -- (void)setRoomAvatarImageIn:(MXKImageView*)mxkImageView; - /** Update the room tag. diff --git a/Riot/Categories/MXRoom+Riot.m b/Riot/Categories/MXRoom+Riot.m index d6b2bb350..d6e070c07 100644 --- a/Riot/Categories/MXRoom+Riot.m +++ b/Riot/Categories/MXRoom+Riot.m @@ -23,182 +23,6 @@ @implementation MXRoom (Riot) -#pragma mark - Room avatar - -- (void)setRoomAvatarImageIn:(MXKImageView*)mxkImageView -{ - NSString* roomAvatarUrl = self.state.avatar; - - if (!roomAvatarUrl) - { - // If the room has only two members, use the avatar of the second member. - NSArray* members = self.state.members; - - if (members.count == 2) - { - NSString* myUserId = self.mxSession.myUser.userId; - - for (MXRoomMember *roomMember in members) - { - if (![roomMember.userId isEqualToString:myUserId]) - { - // Use the avatar of this member only if he joined or he is invited. - if (MXMembershipJoin == roomMember.membership || MXMembershipInvite == roomMember.membership) - { - roomAvatarUrl = roomMember.avatarUrl; - } - break; - } - } - } - } - - // Retrieve the Riot room display name to prepare the default avatar image. - // Note: this display name is nil for an "empty room" without display name (We name "empty room" a room in which the current user is the only active member). - NSString *avatarDisplayName = self.riotDisplayname; - UIImage* avatarImage = [AvatarGenerator generateAvatarForMatrixItem:self.state.roomId withDisplayName:avatarDisplayName]; - - if (roomAvatarUrl) - { - mxkImageView.enableInMemoryCache = YES; - - [mxkImageView setImageURL:[self.mxSession.matrixRestClient urlOfContentThumbnail:roomAvatarUrl toFitViewSize:mxkImageView.frame.size withMethod:MXThumbnailingMethodCrop] withType:nil andImageOrientation:UIImageOrientationUp previewImage:avatarImage]; - } - else - { - mxkImageView.image = avatarImage; - } - - mxkImageView.contentMode = UIViewContentModeScaleAspectFill; -} - -#pragma mark - Room display name -// @TODO: May worth to refactor to use MXRoomSummary -- (NSString *)riotDisplayname -{ - // this algo is the one defined in - // https://github.com/matrix-org/matrix-js-sdk/blob/develop/lib/models/room.js#L617 - // calculateRoomName(room, userId) - - MXRoomState* roomState = self.state; - - if (roomState.name.length > 0) - { - return roomState.name; - } - - NSString *alias = roomState.canonicalAlias; - - if (!alias) - { - // For rooms where canonical alias is not defined, we use the 1st alias as a workaround - NSArray *aliases = roomState.aliases; - - if (aliases.count) - { - alias = [aliases[0] copy]; - } - } - - // check if there is non empty alias. - if ([alias length] > 0) - { - return alias; - } - - NSString* myUserId = self.mxSession.myUser.userId; - - NSArray* members = roomState.members; - NSMutableArray* othersActiveMembers = [[NSMutableArray alloc] init]; - NSMutableArray* activeMembers = [[NSMutableArray alloc] init]; - - for(MXRoomMember* member in members) - { - if (member.membership != MXMembershipLeave) - { - if (![member.userId isEqualToString:myUserId]) - { - [othersActiveMembers addObject:member]; - } - - [activeMembers addObject:member]; - } - } - - // sort the members by their creation (oldest first) - othersActiveMembers = [[othersActiveMembers sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { - - uint64_t originServerTs1 = 0; - uint64_t originServerTs2 = 0; - - MXRoomMember* member1 = (MXRoomMember*)obj1; - MXRoomMember* member2 = (MXRoomMember*)obj2; - - if (member1.originalEvent) - { - originServerTs1 = member1.originalEvent.originServerTs; - } - - if (member2.originalEvent) - { - originServerTs2 = member2.originalEvent.originServerTs; - } - - if (originServerTs1 == originServerTs2) - { - return NSOrderedSame; - } - else - { - return originServerTs1 > originServerTs2 ? NSOrderedDescending : NSOrderedAscending; - } - }] mutableCopy]; - - - NSString* displayName = @""; - - if (othersActiveMembers.count == 0) - { - if (activeMembers.count == 1) - { - MXRoomMember* member = [activeMembers objectAtIndex:0]; - - if (member.membership == MXMembershipInvite) - { - if (member.originalEvent.sender) - { - // extract who invited us to the room - displayName = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_displayname_invite_from", @"Vector", nil), [roomState memberName:member.originalEvent.sender]]; - } - else - { - displayName = NSLocalizedStringFromTable(@"room_displayname_room_invite", @"Vector", nil); - } - } - } - } - else if (othersActiveMembers.count == 1) - { - MXRoomMember* member = [othersActiveMembers objectAtIndex:0]; - - displayName = [roomState memberName:member.userId]; - } - else if (othersActiveMembers.count == 2) - { - MXRoomMember* member1 = [othersActiveMembers objectAtIndex:0]; - MXRoomMember* member2 = [othersActiveMembers objectAtIndex:1]; - - displayName = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_displayname_two_members", @"Vector", nil), [roomState memberName:member1.userId], [roomState memberName:member2.userId]]; - } - else - { - MXRoomMember* member = [othersActiveMembers objectAtIndex:0]; - displayName = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_displayname_more_than_two_members", @"Vector", nil), [roomState memberName:member.userId], othersActiveMembers.count - 1]; - } - - return displayName; -} - #pragma mark - Room tags - (void)setRoomTag:(NSString*)tag completion:(void (^)())completion diff --git a/Riot/Categories/MXRoomSummary+Riot.h b/Riot/Categories/MXRoomSummary+Riot.h new file mode 100644 index 000000000..804a6a6be --- /dev/null +++ b/Riot/Categories/MXRoomSummary+Riot.h @@ -0,0 +1,35 @@ +/* + 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 + +/** + Define a `MXRoomSummary` category at Riot level. + */ +@interface MXRoomSummary (Riot) + +/** + Set the room avatar in the dedicated MXKImageView. + The riot style implies to use in order : + 1 - the default avatar if there is one + 2 - the member avatar for < 3 members rooms + 3 - the first letter of the room name. + + @param mxkImageView the destinated MXKImageView. + */ +- (void)setRoomAvatarImageIn:(MXKImageView*)mxkImageView; + +@end diff --git a/Riot/Categories/MXRoomSummary+Riot.m b/Riot/Categories/MXRoomSummary+Riot.m new file mode 100644 index 000000000..78465b325 --- /dev/null +++ b/Riot/Categories/MXRoomSummary+Riot.m @@ -0,0 +1,43 @@ +/* + 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 "MXRoomSummary+Riot.h" + +#import "AvatarGenerator.h" + +@implementation MXRoomSummary (Riot) + +- (void)setRoomAvatarImageIn:(MXKImageView*)mxkImageView +{ + // Use the room display name to prepare the default avatar image. + NSString *avatarDisplayName = self.displayname; + UIImage* avatarImage = [AvatarGenerator generateAvatarForMatrixItem:self.roomId withDisplayName:avatarDisplayName]; + + if (self.avatar) + { + mxkImageView.enableInMemoryCache = YES; + + [mxkImageView setImageURL:[self.mxSession.matrixRestClient urlOfContentThumbnail:self.avatar toFitViewSize:mxkImageView.frame.size withMethod:MXThumbnailingMethodCrop] withType:nil andImageOrientation:UIImageOrientationUp previewImage:avatarImage]; + } + else + { + mxkImageView.image = avatarImage; + } + + mxkImageView.contentMode = UIViewContentModeScaleAspectFill; +} + +@end diff --git a/Riot/Categories/UIViewController+RiotSearch.m b/Riot/Categories/UIViewController+RiotSearch.m index 9db702f76..becfd114c 100644 --- a/Riot/Categories/UIViewController+RiotSearch.m +++ b/Riot/Categories/UIViewController+RiotSearch.m @@ -134,7 +134,8 @@ - (void)addBackgroundImageViewToView:(UIView*)view { - UIImageView *backgroundImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"search_bg"]]; + UIImage *searchBgImage = [MXKTools paintImage:[UIImage imageNamed:@"search_bg"] withColor:kRiotKeyboardColor]; + UIImageView *backgroundImageView = [[UIImageView alloc] initWithImage:searchBgImage]; backgroundImageView.translatesAutoresizingMaskIntoConstraints = NO; [view addSubview:backgroundImageView]; diff --git a/Riot/Model/Contact/ContactsDataSource.m b/Riot/Model/Contact/ContactsDataSource.m index bf6b9c054..657878d01 100644 --- a/Riot/Model/Contact/ContactsDataSource.m +++ b/Riot/Model/Contact/ContactsDataSource.m @@ -746,7 +746,7 @@ attributes:@{NSForegroundColorAttributeName : kRiotPrimaryTextColor, NSFontAttributeName: [UIFont boldSystemFontOfSize:15.0]}]; [mutableSectionTitle appendAttributedString:[[NSMutableAttributedString alloc] initWithString:roomCount - attributes:@{NSForegroundColorAttributeName : kRiotColorSilver, + attributes:@{NSForegroundColorAttributeName : kRiotAuxiliaryColor, NSFontAttributeName: [UIFont boldSystemFontOfSize:15.0]}]]; sectionTitle = mutableSectionTitle; diff --git a/Riot/Model/Room/RoomPreviewData.m b/Riot/Model/Room/RoomPreviewData.m index e03cae846..39f40d2ba 100644 --- a/Riot/Model/Room/RoomPreviewData.m +++ b/Riot/Model/Room/RoomPreviewData.m @@ -17,8 +17,6 @@ #import "RoomPreviewData.h" -#import "MXRoom+Riot.h" - @implementation RoomPreviewData - (instancetype)initWithRoomId:(NSString *)roomId andSession:(MXSession *)mxSession @@ -88,8 +86,8 @@ [_roomDataSource finalizeInitialization]; _roomDataSource.markTimelineInitialEvent = YES; - _roomName = peekingRoom.riotDisplayname; - _roomAvatarUrl = peekingRoom.state.avatar; + _roomName = peekingRoom.summary.displayname; + _roomAvatarUrl = peekingRoom.summary.avatar; _roomTopic = [MXTools stripNewlineCharacters:peekingRoom.state.topic];; _roomAliases = peekingRoom.state.aliases; diff --git a/Riot/Model/RoomList/RecentCellData.m b/Riot/Model/RoomList/RecentCellData.m index b1c8623ca..6b28e9528 100644 --- a/Riot/Model/RoomList/RecentCellData.m +++ b/Riot/Model/RoomList/RecentCellData.m @@ -57,7 +57,7 @@ - (void)update { [super update]; - roomDisplayname = self.roomSummary.room.riotDisplayname; + roomDisplayname = self.roomSummary.displayname; if (!roomDisplayname.length) { roomDisplayname = NSLocalizedStringFromTable(@"room_displayname_no_title", @"Vector", nil); diff --git a/Riot/Model/RoomList/RecentsDataSource.m b/Riot/Model/RoomList/RecentsDataSource.m index 4fa6e1065..83c725b59 100644 --- a/Riot/Model/RoomList/RecentsDataSource.m +++ b/Riot/Model/RoomList/RecentsDataSource.m @@ -398,7 +398,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou attributes:@{NSForegroundColorAttributeName : kRiotPrimaryTextColor, NSFontAttributeName: [UIFont boldSystemFontOfSize:15.0]}]; [mutableSectionTitle appendAttributedString:[[NSMutableAttributedString alloc] initWithString:roomCount - attributes:@{NSForegroundColorAttributeName : kRiotColorSilver, + attributes:@{NSForegroundColorAttributeName : kRiotAuxiliaryColor, NSFontAttributeName: [UIFont boldSystemFontOfSize:15.0]}]]; sectionTitle = mutableSectionTitle; @@ -463,7 +463,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou missedNotifAndUnreadBadgeBgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, bgViewWidth, 20)]; [missedNotifAndUnreadBadgeBgView.layer setCornerRadius:10]; - missedNotifAndUnreadBadgeBgView.backgroundColor = kRiotColorSilver; + missedNotifAndUnreadBadgeBgView.backgroundColor = kRiotAuxiliaryColor; [missedNotifAndUnreadBadgeBgView addSubview:missedNotifAndUnreadBadgeLabel]; missedNotifAndUnreadBadgeLabel.center = missedNotifAndUnreadBadgeBgView.center; @@ -1487,7 +1487,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou NSString* tagOrder = [room.mxSession tagOrderToBeAtIndex:newPath.row from:oldPos withTag:dstRoomTag]; - NSLog(@"[RecentsDataSource] Update the room %@ [%@] tag from %@ to %@ with tag order %@", room.state.roomId, room.riotDisplayname, oldRoomTag, dstRoomTag, tagOrder); + NSLog(@"[RecentsDataSource] Update the room %@ [%@] tag from %@ to %@ with tag order %@", room.state.roomId, room.summary.displayname, oldRoomTag, dstRoomTag, tagOrder); [room replaceTag:oldRoomTag byTag:dstRoomTag diff --git a/Riot/Model/Search/FilesSearchCellData.m b/Riot/Model/Search/FilesSearchCellData.m index 6ad642147..3eff018a7 100644 --- a/Riot/Model/Search/FilesSearchCellData.m +++ b/Riot/Model/Search/FilesSearchCellData.m @@ -17,8 +17,6 @@ #import "FilesSearchCellData.h" -#import "MXRoom+Riot.h" - @implementation FilesSearchCellData @synthesize roomId, senderDisplayName; @synthesize searchResult, title, message, date, shouldShowRoomDisplayName, roomDisplayName, attachment, isAttachmentWithThumbnail, attachmentIcon; @@ -82,7 +80,7 @@ MXRoom *room = [searchDataSource.mxSession roomWithRoomId:roomId]; if (room) { - roomDisplayName = room.riotDisplayname; + roomDisplayName = room.summary.displayname; if (!roomDisplayName.length) { roomDisplayName = NSLocalizedStringFromTable(@"room_displayname_no_title", @"Vector", nil); diff --git a/Riot/Riot-Defaults.plist b/Riot/Riot-Defaults.plist index 7b9df11ce..da87bc19d 100644 --- a/Riot/Riot-Defaults.plist +++ b/Riot/Riot-Defaults.plist @@ -25,9 +25,9 @@ webAppUrl https://riot.im/app integrationsUiUrl - https://scalar.vector.im/ + https://scalar-staging.riot.im/scalar-web/ integrationsRestUrl - https://scalar.vector.im/api + https://scalar-staging.riot.im/scalar/api apnsDeviceToken showAllEventsInRoomHistory diff --git a/Riot/Utils/EventFormatter.m b/Riot/Utils/EventFormatter.m index 6fb500e2d..792de6359 100644 --- a/Riot/Utils/EventFormatter.m +++ b/Riot/Utils/EventFormatter.m @@ -190,6 +190,225 @@ return senderAvatarUrl; } +#pragma mark - MXRoomSummaryUpdating + +- (BOOL)session:(MXSession *)session updateRoomSummary:(MXRoomSummary *)summary withStateEvents:(NSArray *)stateEvents +{ + BOOL ret = [super session:session updateRoomSummary:summary withStateEvents:stateEvents]; + + // Check whether the room display name and/or the room avatar url should be updated at Riot level. + NSString *riotRoomDisplayName; + NSString *riotRoomAvatarURL; + + for (MXEvent *event in stateEvents) + { + switch (event.eventType) + { + case MXEventTypeRoomName: + case MXEventTypeRoomAliases: + case MXEventTypeRoomCanonicalAlias: + { + if (!riotRoomDisplayName.length) + { + riotRoomDisplayName = [self riotRoomDisplayNameFromRoomState:summary.room.state]; + } + break; + } + case MXEventTypeRoomMember: + { + if (!riotRoomDisplayName.length) + { + riotRoomDisplayName = [self riotRoomDisplayNameFromRoomState:summary.room.state]; + } + // Do not break here to check avatar url too. + } + case MXEventTypeRoomAvatar: + { + if (!riotRoomAvatarURL.length) + { + riotRoomAvatarURL = [self riotRoomAvatarURLFromRoomState:summary.room.state]; + } + break; + } + default: + break; + } + } + + if (riotRoomDisplayName.length && ![summary.displayname isEqualToString:riotRoomDisplayName]) + { + summary.displayname = riotRoomDisplayName; + ret = YES; + } + + if (riotRoomAvatarURL.length && ![summary.avatar isEqualToString:riotRoomAvatarURL]) + { + summary.avatar = riotRoomAvatarURL; + ret = YES; + } + + return ret; +} + +#pragma mark - Riot room display name + +- (NSString *)riotRoomDisplayNameFromRoomState:(MXRoomState *)roomState +{ + // this algo is the one defined in + // https://github.com/matrix-org/matrix-js-sdk/blob/develop/lib/models/room.js#L617 + // calculateRoomName(room, userId) + + // This display name is @"" for an "empty room" without display name (We name "empty room" a room in which the current user is the only active member). + + if (roomState.name.length > 0) + { + return roomState.name; + } + + NSString *alias = roomState.canonicalAlias; + + if (!alias) + { + // For rooms where canonical alias is not defined, we use the 1st alias as a workaround + NSArray *aliases = roomState.aliases; + + if (aliases.count) + { + alias = [aliases[0] copy]; + } + } + + // check if there is non empty alias. + if ([alias length] > 0) + { + return alias; + } + + NSString* myUserId = mxSession.myUser.userId; + + NSArray* members = roomState.members; + NSMutableArray* othersActiveMembers = [[NSMutableArray alloc] init]; + NSMutableArray* activeMembers = [[NSMutableArray alloc] init]; + + for(MXRoomMember* member in members) + { + if (member.membership != MXMembershipLeave) + { + if (![member.userId isEqualToString:myUserId]) + { + [othersActiveMembers addObject:member]; + } + + [activeMembers addObject:member]; + } + } + + // sort the members by their creation (oldest first) + othersActiveMembers = [[othersActiveMembers sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { + + uint64_t originServerTs1 = 0; + uint64_t originServerTs2 = 0; + + MXRoomMember* member1 = (MXRoomMember*)obj1; + MXRoomMember* member2 = (MXRoomMember*)obj2; + + if (member1.originalEvent) + { + originServerTs1 = member1.originalEvent.originServerTs; + } + + if (member2.originalEvent) + { + originServerTs2 = member2.originalEvent.originServerTs; + } + + if (originServerTs1 == originServerTs2) + { + return NSOrderedSame; + } + else + { + return originServerTs1 > originServerTs2 ? NSOrderedDescending : NSOrderedAscending; + } + }] mutableCopy]; + + + NSString* displayName = @""; + + if (othersActiveMembers.count == 0) + { + if (activeMembers.count == 1) + { + MXRoomMember* member = [activeMembers objectAtIndex:0]; + + if (member.membership == MXMembershipInvite) + { + if (member.originalEvent.sender) + { + // extract who invited us to the room + displayName = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_displayname_invite_from", @"Vector", nil), [roomState memberName:member.originalEvent.sender]]; + } + else + { + displayName = NSLocalizedStringFromTable(@"room_displayname_room_invite", @"Vector", nil); + } + } + } + } + else if (othersActiveMembers.count == 1) + { + MXRoomMember* member = [othersActiveMembers objectAtIndex:0]; + + displayName = [roomState memberName:member.userId]; + } + else if (othersActiveMembers.count == 2) + { + MXRoomMember* member1 = [othersActiveMembers objectAtIndex:0]; + MXRoomMember* member2 = [othersActiveMembers objectAtIndex:1]; + + displayName = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_displayname_two_members", @"Vector", nil), [roomState memberName:member1.userId], [roomState memberName:member2.userId]]; + } + else + { + MXRoomMember* member = [othersActiveMembers objectAtIndex:0]; + displayName = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_displayname_more_than_two_members", @"Vector", nil), [roomState memberName:member.userId], othersActiveMembers.count - 1]; + } + + return displayName; +} + +#pragma mark - Riot room avatar url + +- (NSString *)riotRoomAvatarURLFromRoomState:(MXRoomState *)roomState +{ + NSString* roomAvatarUrl = roomState.avatar; + + if (!roomAvatarUrl) + { + // If the room has only two members, use the avatar of the second member. + NSArray* members = roomState.members; + + if (members.count == 2) + { + NSString* myUserId = mxSession.myUser.userId; + + for (MXRoomMember *roomMember in members) + { + if (![roomMember.userId isEqualToString:myUserId]) + { + // Use the avatar of this member only if he joined or he is invited. + if (MXMembershipJoin == roomMember.membership || MXMembershipInvite == roomMember.membership) + { + roomAvatarUrl = roomMember.avatarUrl; + } + break; + } + } + } + } + + return roomAvatarUrl; +} #pragma mark - Timestamp formatting diff --git a/Riot/Utils/RiotDesignValues.h b/Riot/Utils/RiotDesignValues.h index d379ebbab..034a28ef5 100644 --- a/Riot/Utils/RiotDesignValues.h +++ b/Riot/Utils/RiotDesignValues.h @@ -39,6 +39,9 @@ extern UIColor *kRiotSecondaryTextColor; //subtitle, sending messages color. extern UIColor *kRiotPlaceholderTextColor; // nil is used to keep the default color. extern UIColor *kRiotTopicTextColor; extern UIColor *kRiotSelectedBgColor; // nil is used to keep the default color. +extern UIColor *kRiotAuxiliaryColor; // kRiotColorSilver by default. +extern UIColor *kRiotOverlayColor; // fading behind dialog modals. This color includes the transparency value. +extern UIColor *kRiotKeyboardColor; #pragma mark - Riot Colors extern UIColor *kRiotColorGreen; diff --git a/Riot/Utils/RiotDesignValues.m b/Riot/Utils/RiotDesignValues.m index 17db34b04..cd0ee8756 100644 --- a/Riot/Utils/RiotDesignValues.m +++ b/Riot/Utils/RiotDesignValues.m @@ -26,6 +26,9 @@ UIColor *kRiotSecondaryTextColor; UIColor *kRiotPlaceholderTextColor; UIColor *kRiotTopicTextColor; UIColor *kRiotSelectedBgColor; +UIColor *kRiotAuxiliaryColor; +UIColor *kRiotOverlayColor; +UIColor *kRiotKeyboardColor; // Riot Colors UIColor *kRiotColorGreen; @@ -42,6 +45,8 @@ UIColor *kRiotBgColorWhite; UIColor *kRiotBgColorBlack; UIColor *kRiotColorLightGrey; UIColor *kRiotColorLightBlack; +UIColor *kRiotColorLightKeyboard; +UIColor *kRiotColorDarkKeyboard; // Riot Text Colors UIColor *kRiotTextColorBlack; @@ -90,6 +95,9 @@ UIColor *kRiotDesignSearchBarTintColor = nil; kRiotColorLightGrey = UIColorFromRGB(0xF2F2F2); kRiotColorLightBlack = UIColorFromRGB(0x353535); + + kRiotColorLightKeyboard = UIColorFromRGB(0xE7E7E7); + kRiotColorDarkKeyboard = UIColorFromRGB(0x7E7E7E); kRiotTextColorBlack = UIColorFromRGB(0x3C3C3C); kRiotTextColorDarkGray = UIColorFromRGB(0x4A4A4A); @@ -135,6 +143,10 @@ UIColor *kRiotDesignSearchBarTintColor = nil; kRiotDesignStatusBarStyle = UIStatusBarStyleLightContent; kRiotDesignSearchBarStyle = UIBarStyleBlack; kRiotDesignSearchBarTintColor = kRiotColorGreen; + + kRiotAuxiliaryColor = kRiotTextColorGray; + kRiotOverlayColor = [UIColor colorWithWhite:0.3 alpha:0.5]; + kRiotKeyboardColor = kRiotColorDarkKeyboard; } else { @@ -150,6 +162,10 @@ UIColor *kRiotDesignSearchBarTintColor = nil; kRiotDesignStatusBarStyle = UIStatusBarStyleDefault; kRiotDesignSearchBarStyle = UIBarStyleDefault; kRiotDesignSearchBarTintColor = nil; // Default tint color. + + kRiotAuxiliaryColor = kRiotColorSilver; + kRiotOverlayColor = [UIColor colorWithWhite:0.7 alpha:0.5]; + kRiotKeyboardColor = kRiotColorLightKeyboard; } [[NSNotificationCenter defaultCenter] postNotificationName:kRiotDesignValuesDidChangeThemeNotification object:nil]; diff --git a/Riot/Utils/Widgets/Widget.m b/Riot/Utils/Widgets/Widget.m index 381e23c48..ce3ffbbc4 100644 --- a/Riot/Utils/Widgets/Widget.m +++ b/Riot/Utils/Widgets/Widget.m @@ -46,6 +46,9 @@ 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 + _url = [_url stringByAppendingString:[NSString stringWithFormat:@"&scalar_token=%@", [[WidgetManager sharedManager] scalarTokenForMXSession:mxSession]]]; } return self; diff --git a/Riot/Utils/Widgets/WidgetManager.h b/Riot/Utils/Widgets/WidgetManager.h index 4a0833e02..77b78340b 100644 --- a/Riot/Utils/Widgets/WidgetManager.h +++ b/Riot/Utils/Widgets/WidgetManager.h @@ -139,4 +139,38 @@ WidgetManagerErrorCode; - (void)addMatrixSession:(MXSession*)mxSession; - (void)removeMatrixSession:(MXSession*)mxSession; +/** + Delete the data associated with an user. + +@param userId the id of the user. + */ +- (void)deleteDataForUser:(NSString*)userId; + +#pragma mark - Modular interface + +/** + Make sure there is a scalar token for the given Matrix session. + + If no token was gotten and stored before, the operation will make http requests + to get one. + + @param mxSession the session to check. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. + */ +- (MXHTTPOperation *)getScalarTokenForMXSession:(MXSession*)mxSession + success:(void (^)(NSString *scalarToken))success + failure:(void (^)(NSError *error))failure; + +/** + The current scalar token for the given Matrix session. + + It may be nil if `getScalarTokenForMXSession` was never called before. + + @param mxSession the session to check. + @return the current scalar token . + */ +- (NSString *)scalarTokenForMXSession:(MXSession*)mxSession; + @end diff --git a/Riot/Utils/Widgets/WidgetManager.m b/Riot/Utils/Widgets/WidgetManager.m index 1dbf43c80..169ca5c1e 100644 --- a/Riot/Utils/Widgets/WidgetManager.m +++ b/Riot/Utils/Widgets/WidgetManager.m @@ -16,6 +16,8 @@ #import "WidgetManager.h" +#import "MXKAppSettings.h" + #pragma mark - Contants NSString *const kWidgetEventTypeString = @"im.vector.modular.widgets"; @@ -40,6 +42,9 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; // MXSession kind of hash -> (Widget id -> `createWidget:` failure block). NSMutableDictionary*> *failureBlockForWidgetCreation; + + // User id -> scalar token + NSMutableDictionary *scalarTokens; } @end @@ -66,6 +71,13 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; widgetEventListener = [NSMutableDictionary dictionary]; successBlockForWidgetCreation = [NSMutableDictionary dictionary]; failureBlockForWidgetCreation = [NSMutableDictionary dictionary]; + + [self load]; + + if (!scalarTokens) + { + scalarTokens = [NSMutableDictionary dictionary]; + } } return self; } @@ -157,6 +169,10 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; return nil; } + NSString *hash = [NSString stringWithFormat:@"%p", room.mxSession]; + successBlockForWidgetCreation[hash][widgetId] = success; + failureBlockForWidgetCreation[hash][widgetId] = failure; + // Send a state event with the widget data // TODO: This API will be shortly replaced by a pure modular API return [room sendStateEventOfType:kWidgetEventTypeString @@ -187,10 +203,6 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; NSString *modularRestUrl = [[NSUserDefaults standardUserDefaults] objectForKey:@"integrationsRestUrl"]; NSString *url = [NSString stringWithFormat:@"%@/widgets/jitsi.html?confId=%@&isAudioConf=%@&displayName=$matrix_display_name&avatarUrl=$matrix_avatar_url&email=$matrix_user_id@", modularRestUrl, confId, video ? @"false" : @"true"]; - NSString *hash = [NSString stringWithFormat:@"%p", room.mxSession]; - successBlockForWidgetCreation[hash][widgetId] = success; - failureBlockForWidgetCreation[hash][widgetId] = failure; - return [self createWidget:widgetId withContent:@{ @"url": url, @@ -327,4 +339,100 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; [failureBlockForWidgetCreation removeObjectForKey:hash]; } +- (void)deleteDataForUser:(NSString *)userId +{ + [scalarTokens removeObjectForKey:userId]; + [self save]; +} + +#pragma mark - Modular interface + +- (NSString *)scalarTokenForMXSession:(MXSession *)mxSession +{ + return scalarTokens[mxSession.myUser.userId]; +} + +- (MXHTTPOperation *)getScalarTokenForMXSession:(MXSession*)mxSession + success:(void (^)(NSString *scalarToken))success + failure:(void (^)(NSError *error))failure; +{ + MXHTTPOperation *operation; + + __block NSString *scalarToken = [self scalarTokenForMXSession:mxSession]; + if (scalarToken) + { + success(scalarToken); + } + else + { + __weak __typeof__(self) weakSelf = self; + + operation = [mxSession.matrixRestClient openIdToken:^(MXOpenIdToken *tokenObject) { + + typeof(self) self = weakSelf; + + if (self) + { + // Exchange the token for a scalar token + NSString *modularRestUrl = [[NSUserDefaults standardUserDefaults] objectForKey:@"integrationsRestUrl"]; + + MXHTTPClient *httpClient = [[MXHTTPClient alloc] initWithBaseURL:modularRestUrl andOnUnrecognizedCertificateBlock:nil]; + + MXHTTPOperation *operation2 = [httpClient requestWithMethod:@"POST" + path:@"register" + parameters:tokenObject.JSONDictionary + success:^(NSDictionary *JSONResponse) { + + MXJSONModelSetString(scalarToken, JSONResponse[@"scalar_token"]) + self->scalarTokens[mxSession.myUser.userId] = scalarToken; + + [self save]; + + if (success) + { + success(scalarToken); + } + + } failure:^(NSError *error) { + NSLog(@"[WidgetManager] getScalarTokenForMXSession. Error in modular/register request"); + + if (failure) + { + failure(error); + } + }]; + + [operation mutateTo:operation2]; + + } + + } failure:^(NSError *error) { + NSLog(@"[WidgetManager] getScalarTokenForMXSession. Error in openIdToken request"); + + if (failure) + { + failure(error); + } + }]; + } + + return operation; +} + +#pragma mark - Private methods + +- (void)load +{ + NSUserDefaults *userDefaults = [MXKAppSettings standardAppSettings].sharedUserDefaults; + scalarTokens = [NSMutableDictionary dictionaryWithDictionary:[userDefaults objectForKey:@"scalarTokens"]]; +} + +- (void)save +{ + NSUserDefaults *userDefaults = [MXKAppSettings standardAppSettings].sharedUserDefaults; + + [userDefaults setObject:scalarTokens forKey:@"scalarTokens"]; + [userDefaults synchronize]; +} + @end diff --git a/Riot/ViewController/AttachmentsViewController.m b/Riot/ViewController/AttachmentsViewController.m index dae75ec77..3fd4dec11 100644 --- a/Riot/ViewController/AttachmentsViewController.m +++ b/Riot/ViewController/AttachmentsViewController.m @@ -61,6 +61,11 @@ self.view.backgroundColor = kRiotPrimaryBgColor; self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; + + self.navigationBar.tintColor = kRiotSecondaryBgColor; + self.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName: kRiotPrimaryTextColor}; + self.backButton.tintColor = kRiotColorGreen; } - (void)viewWillAppear:(BOOL)animated diff --git a/Riot/ViewController/AuthenticationViewController.m b/Riot/ViewController/AuthenticationViewController.m index bce884a06..e7b6d3789 100644 --- a/Riot/ViewController/AuthenticationViewController.m +++ b/Riot/ViewController/AuthenticationViewController.m @@ -176,6 +176,7 @@ self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; [self.authInputsView customizeViewRendering]; @@ -722,6 +723,27 @@ // Hide the custom server details in order to save customized inputs [self hideCustomServers:YES]; + // Create DM with Riot-bot on new account creation. + if (self.authType == MXKAuthenticationTypeRegister) + { + MXKAccount *account = [[MXKAccountManager sharedManager] accountForUserId:userId]; + + [account.mxSession createRoom:nil + visibility:kMXRoomDirectoryVisibilityPrivate + roomAlias:nil + topic:nil + invite:@[@"@riot-bot:matrix.org"] + invite3PID:nil + isDirect:YES + preset:kMXRoomPresetTrustedPrivateChat + success:nil + failure:^(NSError *error) { + + NSLog(@"[AuthenticationVC] Create chat with riot-bot failed"); + + }]; + } + // Remove auth view controller on successful login if (self.navigationController) { diff --git a/Riot/ViewController/BugReportViewController.m b/Riot/ViewController/BugReportViewController.m index b6ddec0e8..cc0a332d6 100644 --- a/Riot/ViewController/BugReportViewController.m +++ b/Riot/ViewController/BugReportViewController.m @@ -34,6 +34,8 @@ @property (nonatomic) BOOL sendLogs; @property (nonatomic) BOOL sendScreenshot; +@property (weak, nonatomic) IBOutlet UIView *overlayView; + @end @implementation BugReportViewController @@ -137,6 +139,10 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; + + self.overlayView.backgroundColor = kRiotOverlayColor; + self.overlayView.alpha = 1.0; self.containerView.backgroundColor = kRiotPrimaryBgColor; self.sendingContainer.backgroundColor = kRiotPrimaryBgColor; diff --git a/Riot/ViewController/BugReportViewController.xib b/Riot/ViewController/BugReportViewController.xib index 65445af46..aa9d51d2c 100644 --- a/Riot/ViewController/BugReportViewController.xib +++ b/Riot/ViewController/BugReportViewController.xib @@ -1,5 +1,5 @@ - + @@ -17,6 +17,7 @@ + diff --git a/Riot/ViewController/CallViewController.m b/Riot/ViewController/CallViewController.m index 558039dfc..9e55b5cb7 100644 --- a/Riot/ViewController/CallViewController.m +++ b/Riot/ViewController/CallViewController.m @@ -21,8 +21,6 @@ #import "AvatarGenerator.h" -#import "MXRoom+Riot.h" - #import "UsersDevicesViewController.h" #import "RiotNavigationController.h" @@ -101,6 +99,7 @@ self.view.backgroundColor = kRiotPrimaryBgColor; self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; self.callerNameLabel.textColor = kRiotPrimaryTextColor; self.callStatusLabel.textColor = kRiotTopicTextColor; @@ -358,7 +357,7 @@ } else if (self.mxCall.room) { - return [AvatarGenerator generateAvatarForMatrixItem:self.mxCall.room.roomId withDisplayName:self.mxCall.room.riotDisplayname size:self.callerImageViewWidthConstraint.constant andFontSize:fontSize]; + return [AvatarGenerator generateAvatarForMatrixItem:self.mxCall.room.roomId withDisplayName:self.mxCall.room.summary.displayname size:self.callerImageViewWidthConstraint.constant andFontSize:fontSize]; } return [UIImage imageNamed:@"placeholder"]; @@ -387,7 +386,7 @@ } else if (self.mxCall.isConferenceCall) { - peerDisplayName = self.mxCall.room.riotDisplayname; + peerDisplayName = self.mxCall.room.summary.displayname; peerAvatarURL = self.mxCall.room.state.avatar; } diff --git a/Riot/ViewController/ContactDetailsViewController.m b/Riot/ViewController/ContactDetailsViewController.m index f82834526..fa97fbe79 100644 --- a/Riot/ViewController/ContactDetailsViewController.m +++ b/Riot/ViewController/ContactDetailsViewController.m @@ -224,6 +224,7 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; self.headerView.backgroundColor = kRiotSecondaryBgColor; self.contactNameLabel.textColor = kRiotPrimaryTextColor; diff --git a/Riot/ViewController/ContactsTableViewController.m b/Riot/ViewController/ContactsTableViewController.m index 63de0c874..299bab4f8 100644 --- a/Riot/ViewController/ContactsTableViewController.m +++ b/Riot/ViewController/ContactsTableViewController.m @@ -105,6 +105,7 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; // Check the table view style to select its bg color. self.contactsTableView.backgroundColor = ((self.contactsTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); diff --git a/Riot/ViewController/CountryPickerViewController.m b/Riot/ViewController/CountryPickerViewController.m index 3d6eb1aa0..b371a5bcb 100644 --- a/Riot/ViewController/CountryPickerViewController.m +++ b/Riot/ViewController/CountryPickerViewController.m @@ -73,6 +73,7 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; self.searchBar.barStyle = kRiotDesignSearchBarStyle; self.searchBar.tintColor = kRiotDesignSearchBarTintColor; diff --git a/Riot/ViewController/DirectoryServerPickerViewController.m b/Riot/ViewController/DirectoryServerPickerViewController.m index 9a0de53bd..6beb3bbe2 100644 --- a/Riot/ViewController/DirectoryServerPickerViewController.m +++ b/Riot/ViewController/DirectoryServerPickerViewController.m @@ -121,6 +121,7 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; // Check the table view style to select its bg color. self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); diff --git a/Riot/ViewController/DirectoryViewController.m b/Riot/ViewController/DirectoryViewController.m index 79d106565..f75eae491 100644 --- a/Riot/ViewController/DirectoryViewController.m +++ b/Riot/ViewController/DirectoryViewController.m @@ -72,6 +72,7 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; // Check the table view style to select its bg color. self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); diff --git a/Riot/ViewController/HomeFilesSearchViewController.m b/Riot/ViewController/HomeFilesSearchViewController.m index 885b2ada3..144a3a704 100644 --- a/Riot/ViewController/HomeFilesSearchViewController.m +++ b/Riot/ViewController/HomeFilesSearchViewController.m @@ -72,6 +72,7 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; // Check the table view style to select its bg color. self.searchTableView.backgroundColor = ((self.searchTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); diff --git a/Riot/ViewController/HomeMessagesSearchViewController.m b/Riot/ViewController/HomeMessagesSearchViewController.m index fa5b80729..5f4627530 100644 --- a/Riot/ViewController/HomeMessagesSearchViewController.m +++ b/Riot/ViewController/HomeMessagesSearchViewController.m @@ -79,6 +79,7 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; // Check the table view style to select its bg color. self.searchTableView.backgroundColor = ((self.searchTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); diff --git a/Riot/ViewController/LanguagePickerViewController.m b/Riot/ViewController/LanguagePickerViewController.m index 3db253420..48eb0b335 100644 --- a/Riot/ViewController/LanguagePickerViewController.m +++ b/Riot/ViewController/LanguagePickerViewController.m @@ -70,6 +70,7 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; self.searchBar.barStyle = kRiotDesignSearchBarStyle; self.searchBar.tintColor = kRiotDesignSearchBarTintColor; diff --git a/Riot/ViewController/MediaAlbumContentViewController.m b/Riot/ViewController/MediaAlbumContentViewController.m index cb8cfcd9e..785974fe6 100644 --- a/Riot/ViewController/MediaAlbumContentViewController.m +++ b/Riot/ViewController/MediaAlbumContentViewController.m @@ -114,6 +114,7 @@ self.assetsCollectionView.backgroundColor = kRiotPrimaryBgColor; self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; } - (UIStatusBarStyle)preferredStatusBarStyle diff --git a/Riot/ViewController/MediaPickerViewController.m b/Riot/ViewController/MediaPickerViewController.m index 529ad1e8f..0002f8960 100644 --- a/Riot/ViewController/MediaPickerViewController.m +++ b/Riot/ViewController/MediaPickerViewController.m @@ -183,6 +183,7 @@ static void *RecordingContext = &RecordingContext; { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; self.cameraVideoCaptureProgressView.progressColor = kRiotPrimaryBgColor; self.cameraVideoCaptureProgressView.unprogressColor = [UIColor clearColor]; diff --git a/Riot/ViewController/ReadReceiptsViewController.m b/Riot/ViewController/ReadReceiptsViewController.m index b1ae370eb..28f0a10a5 100644 --- a/Riot/ViewController/ReadReceiptsViewController.m +++ b/Riot/ViewController/ReadReceiptsViewController.m @@ -96,6 +96,10 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; + + self.overlayView.backgroundColor = kRiotOverlayColor; + self.overlayView.alpha = 1.0; self.titleLabel.textColor = kRiotPrimaryTextColor; self.containerView.backgroundColor = kRiotPrimaryBgColor; diff --git a/Riot/ViewController/RecentsViewController.m b/Riot/ViewController/RecentsViewController.m index 0af833690..7a0fd765a 100644 --- a/Riot/ViewController/RecentsViewController.m +++ b/Riot/ViewController/RecentsViewController.m @@ -159,6 +159,7 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; // Use the primary bg color for the recents table view in plain style. self.recentsTableView.backgroundColor = kRiotPrimaryBgColor; diff --git a/Riot/ViewController/RoomFilesSearchViewController.m b/Riot/ViewController/RoomFilesSearchViewController.m index 3f5a050de..c324416e4 100644 --- a/Riot/ViewController/RoomFilesSearchViewController.m +++ b/Riot/ViewController/RoomFilesSearchViewController.m @@ -73,6 +73,7 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; // Check the table view style to select its bg color. self.searchTableView.backgroundColor = ((self.searchTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); diff --git a/Riot/ViewController/RoomFilesViewController.m b/Riot/ViewController/RoomFilesViewController.m index 8b945fbbf..53319ec57 100644 --- a/Riot/ViewController/RoomFilesViewController.m +++ b/Riot/ViewController/RoomFilesViewController.m @@ -111,6 +111,7 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; // Check the table view style to select its bg color. self.bubblesTableView.backgroundColor = ((self.bubblesTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); diff --git a/Riot/ViewController/RoomMemberDetailsViewController.m b/Riot/ViewController/RoomMemberDetailsViewController.m index 661cf8623..9aaed6b90 100644 --- a/Riot/ViewController/RoomMemberDetailsViewController.m +++ b/Riot/ViewController/RoomMemberDetailsViewController.m @@ -209,6 +209,7 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; self.memberHeaderView.backgroundColor = kRiotSecondaryBgColor; self.roomMemberNameLabel.textColor = kRiotPrimaryTextColor; diff --git a/Riot/ViewController/RoomMessagesSearchViewController.m b/Riot/ViewController/RoomMessagesSearchViewController.m index 1b6cecd33..0d1b8f548 100644 --- a/Riot/ViewController/RoomMessagesSearchViewController.m +++ b/Riot/ViewController/RoomMessagesSearchViewController.m @@ -74,6 +74,7 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; // Check the table view style to select its bg color. self.searchTableView.backgroundColor = ((self.searchTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); diff --git a/Riot/ViewController/RoomParticipantsViewController.m b/Riot/ViewController/RoomParticipantsViewController.m index 34d2594ea..f6bd6fea5 100644 --- a/Riot/ViewController/RoomParticipantsViewController.m +++ b/Riot/ViewController/RoomParticipantsViewController.m @@ -158,10 +158,11 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; [self refreshSearchBarItemsColor:_searchBarView]; - _searchBarHeaderBorder.backgroundColor = kRiotColorSilver; + _searchBarHeaderBorder.backgroundColor = kRiotAuxiliaryColor; // Check the table view style to select its bg color. self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); diff --git a/Riot/ViewController/RoomSearchViewController.m b/Riot/ViewController/RoomSearchViewController.m index bdbb2e2fa..f6b7ab50e 100644 --- a/Riot/ViewController/RoomSearchViewController.m +++ b/Riot/ViewController/RoomSearchViewController.m @@ -63,7 +63,7 @@ [super viewDidLoad]; - // Add the Vector background image when search bar is empty + // Add the Riot background image when search bar is empty [self addBackgroundImageViewToView:self.view]; // Initialize here the data sources if a matrix session has been already set. @@ -72,6 +72,18 @@ self.searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone; } +- (void)userInterfaceThemeDidChange +{ + [super userInterfaceThemeDidChange]; + + UIImageView *backgroundImageView = self.backgroundImageView; + if (backgroundImageView) + { + UIImage *image = [MXKTools paintImage:backgroundImageView.image withColor:kRiotKeyboardColor]; + backgroundImageView.image = image; + } +} + - (void)destroy { [super destroy]; diff --git a/Riot/ViewController/RoomSettingsViewController.m b/Riot/ViewController/RoomSettingsViewController.m index 551707ea4..68d3ff632 100644 --- a/Riot/ViewController/RoomSettingsViewController.m +++ b/Riot/ViewController/RoomSettingsViewController.m @@ -26,6 +26,7 @@ #import "Tools.h" #import "MXRoom+Riot.h" +#import "MXRoomSummary+Riot.h" #import "AppDelegate.h" @@ -246,6 +247,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; // Check the table view style to select its bg color. self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); @@ -1964,6 +1966,9 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } cell = roomNotifCell; + + // Force layout before reusing a cell (fix switch displayed outside the screen) + [cell layoutIfNeeded]; } else if (row == ROOM_SETTINGS_MAIN_SECTION_ROW_PHOTO) { @@ -1994,7 +1999,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti } else { - [mxRoom setRoomAvatarImageIn:roomPhotoCell.mxkImageView]; + [mxRoom.summary setRoomAvatarImageIn:roomPhotoCell.mxkImageView]; roomPhotoCell.userInteractionEnabled = (oneSelfPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomAvatar]); roomPhotoCell.mxkImageView.alpha = roomPhotoCell.userInteractionEnabled ? 1.0f : 0.5f; @@ -2181,6 +2186,9 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti directoryVisibilitySwitch.enabled = (oneSelfPowerLevel >= powerLevels.stateDefault); cell = directoryToggleCell; + + // Force layout before reusing a cell (fix switch displayed outside the screen) + [cell layoutIfNeeded]; } else if (indexPath.row == missingAddressWarningIndex) { @@ -2491,6 +2499,9 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti roomEncryptionBlacklistUnverifiedDevicesSwitch.on = blacklistUnverifiedDevices; cell = roomBlacklistUnverifiedDevicesCell; + + // Force layout before reusing a cell (fix switch displayed outside the screen) + [cell layoutIfNeeded]; } else if (indexPath.row == 2) { @@ -2547,6 +2558,9 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti roomEncryptionSwitch.on = ([updatedItemsDict objectForKey:kRoomSettingsEncryptionKey] != nil); cell = roomEncryptionCell; + + // Force layout before reusing a cell (fix switch displayed outside the screen) + [cell layoutIfNeeded]; } else { diff --git a/Riot/ViewController/RoomViewController.m b/Riot/ViewController/RoomViewController.m index 8bb9a5345..04d58bec3 100644 --- a/Riot/ViewController/RoomViewController.m +++ b/Riot/ViewController/RoomViewController.m @@ -109,6 +109,9 @@ #import "MXRoom+Riot.h" +#import "IntegrationManagerViewController.h" +#import "WidgetViewController.h" + @interface RoomViewController () { // The expanded header @@ -180,9 +183,9 @@ // Tell whether the view controller is appeared or not. BOOL isAppeared; - // The search bar buttom item back up. - UIBarButtonItem *searchBarButtonItem; - + // The right bar button items back up. + NSArray *rightBarButtonItems; + // Observe kRiotDesignValuesDidChangeThemeNotification to handle user interface theme change. id kRiotDesignValuesDidChangeThemeNotificationObserver; } @@ -360,9 +363,12 @@ [self setEventDetailsViewClass:EventDetailsView.class]; // Update navigation bar items - self.navigationItem.rightBarButtonItem.target = self; - self.navigationItem.rightBarButtonItem.action = @selector(onButtonPressed:); - + for (UIBarButtonItem *barButtonItem in self.navigationItem.rightBarButtonItems) + { + barButtonItem.target = self; + barButtonItem.action = @selector(onButtonPressed:); + } + // Prepare missed dicussion badge missedDiscussionsBarButtonCustomView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 21)]; missedDiscussionsBarButtonCustomView.backgroundColor = [UIColor clearColor]; @@ -417,6 +423,7 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; // Prepare jump to last unread banner self.jumpToLastUnreadBannerContainer.backgroundColor = kRiotPrimaryBgColor; @@ -1046,8 +1053,11 @@ - (void)destroy { - searchBarButtonItem = nil; - self.navigationItem.rightBarButtonItem.enabled = NO; + rightBarButtonItems = nil; + for (UIBarButtonItem *barButtonItem in self.navigationItem.rightBarButtonItems) + { + barButtonItem.enabled = NO; + } if (currentAlert) { @@ -1150,17 +1160,20 @@ - (void)refreshRoomTitle { - if (searchBarButtonItem && !self.navigationItem.rightBarButtonItem) + if (rightBarButtonItems && !self.navigationItem.rightBarButtonItems) { // Restore by default the search bar button. - self.navigationItem.rightBarButtonItem = searchBarButtonItem; + self.navigationItem.rightBarButtonItems = rightBarButtonItems; } // Set the right room title view if (self.isRoomPreview) { - // Disable the search button - self.navigationItem.rightBarButtonItem.enabled = NO; + // Disable the right buttons + for (UIBarButtonItem *barButtonItem in self.navigationItem.rightBarButtonItems) + { + barButtonItem.enabled = NO; + } [self showPreviewHeader:YES]; } @@ -1170,8 +1183,18 @@ if (self.roomDataSource.isLive) { - // Enable the search button - self.navigationItem.rightBarButtonItem.enabled = YES; + // Enable the right buttons (Search and Integrations) + for (UIBarButtonItem *barButtonItem in self.navigationItem.rightBarButtonItems) + { + barButtonItem.enabled = YES; + } + + BOOL matrixAppsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"matrixApps"]; + if (!matrixAppsEnabled && self.navigationItem.rightBarButtonItems.count == 2) + { + // If the setting is disabled, do not show the icon + self.navigationItem.rightBarButtonItems = @[self.navigationItem.rightBarButtonItem]; + } // Do not change title view class here if the expanded header is visible. if (self.expandedHeaderContainer.hidden) @@ -1191,8 +1214,8 @@ else { // Remove the search button temporarily - searchBarButtonItem = self.navigationItem.rightBarButtonItem; - self.navigationItem.rightBarButtonItem = nil; + rightBarButtonItems = self.navigationItem.rightBarButtonItems; + self.navigationItem.rightBarButtonItems = nil; [self setRoomTitleViewClass:SimpleRoomTitleView.class]; self.titleView.editable = NO; @@ -2850,10 +2873,35 @@ - (IBAction)onButtonPressed:(id)sender { + // Search button if (sender == self.navigationItem.rightBarButtonItem) { [self performSegueWithIdentifier:@"showRoomSearch" sender:self]; } + // Matrix Apps button + else if (self.navigationItem.rightBarButtonItems.count == 2 && sender == self.navigationItem.rightBarButtonItems[1]) + { + // Temporary code to test `WidgetViewController` + // TODO: remove it +// NSArray *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 + { + IntegrationManagerViewController *modularVC = [[IntegrationManagerViewController alloc] initForMXSession:self.roomDataSource.mxSession + inRoom:self.roomDataSource.roomId + screen:kIntegrationManagerMainScreen + widgetId:nil]; + + [self presentViewController:modularVC animated:NO completion:nil]; + } + } else if (sender == self.jumpToLastUnreadButton) { // Hide expanded header to restore navigation bar settings. diff --git a/Riot/ViewController/SegmentedViewController.h b/Riot/ViewController/SegmentedViewController.h index b870f188c..b6949a7c9 100644 --- a/Riot/ViewController/SegmentedViewController.h +++ b/Riot/ViewController/SegmentedViewController.h @@ -77,6 +77,11 @@ limitations under the License. @param viewControllers the list of viewControllers to display. @param defaultSelected index of the default selected UIViewController in the list. */ -- (void)initWithTitles:(NSArray*)titles viewControllers:(NSArray*)viewControllers defaultSelected:(NSUInteger)index; +- (void)initWithTitles:(NSArray*)titles viewControllers:(NSArray*)viewControllers defaultSelected:(NSUInteger)defaultSelected; + +/** + Callback used to take into account the change of the user interface theme. + */ +- (void)userInterfaceThemeDidChange; @end diff --git a/Riot/ViewController/SegmentedViewController.m b/Riot/ViewController/SegmentedViewController.m index 41415344b..0d49ed60c 100644 --- a/Riot/ViewController/SegmentedViewController.m +++ b/Riot/ViewController/SegmentedViewController.m @@ -71,11 +71,11 @@ @param viewControllers the list of viewControllers to display. @param defaultSelected index of the default selected UIViewController in the list. */ -- (void)initWithTitles:(NSArray*)titles viewControllers:(NSArray*)someViewControllers defaultSelected:(NSUInteger)index +- (void)initWithTitles:(NSArray*)titles viewControllers:(NSArray*)someViewControllers defaultSelected:(NSUInteger)defaultSelected { viewControllers = someViewControllers; sectionTitles = titles; - _selectedIndex = index; + _selectedIndex = defaultSelected; } - (void)destroy @@ -172,6 +172,7 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; self.view.backgroundColor = kRiotPrimaryBgColor; } diff --git a/Riot/ViewController/SettingsViewController.m b/Riot/ViewController/SettingsViewController.m index b3ed78ad3..8c5166cd6 100644 --- a/Riot/ViewController/SettingsViewController.m +++ b/Riot/ViewController/SettingsViewController.m @@ -105,8 +105,9 @@ enum enum { -#ifdef USE_JITSI_WIDGET LABS_MATRIX_APPS_INDEX = 0, +#ifdef USE_JITSI_WIDGET + LABS_USE_JITSI_WIDGET_INDEX, #endif LABS_CRYPTO_INDEX, LABS_COUNT @@ -314,6 +315,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; // Check the table view style to select its bg color. self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); @@ -1269,6 +1271,8 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); cell.alpha = 1.0f; cell.userInteractionEnabled = YES; + [cell layoutIfNeeded]; + return cell; } @@ -1281,6 +1285,9 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); cell.mxkLabel.textColor = kRiotPrimaryTextColor; + // Force layout before reusing a cell (fix switch displayed outside the screen) + [cell layoutIfNeeded]; + return cell; } @@ -1331,6 +1338,12 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); { signOutCell = [[MXKTableViewCellWithButton alloc] init]; } + else + { + // Fix https://github.com/vector-im/riot-ios/issues/1354 + // Do not move this line in prepareForReuse because of https://github.com/vector-im/riot-ios/issues/1323 + signOutCell.mxkButton.titleLabel.text = nil; + } NSString* title = NSLocalizedStringFromTable(@"settings_sign_out", @"Vector", nil); @@ -1909,6 +1922,11 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); { markAllBtnCell = [[MXKTableViewCellWithButton alloc] init]; } + else + { + // Fix https://github.com/vector-im/riot-ios/issues/1354 + markAllBtnCell.mxkButton.titleLabel.text = nil; + } NSString *btnTitle = NSLocalizedStringFromTable(@"settings_mark_all_as_read", @"Vector", nil); [markAllBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal]; @@ -1929,6 +1947,11 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); { clearCacheBtnCell = [[MXKTableViewCellWithButton alloc] init]; } + else + { + // Fix https://github.com/vector-im/riot-ios/issues/1354 + clearCacheBtnCell.mxkButton.titleLabel.text = nil; + } NSString *btnTitle = NSLocalizedStringFromTable(@"settings_clear_cache", @"Vector", nil); [clearCacheBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal]; @@ -1949,6 +1972,11 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); { reportBugBtnCell = [[MXKTableViewCellWithButton alloc] init]; } + else + { + // Fix https://github.com/vector-im/riot-ios/issues/1354 + reportBugBtnCell.mxkButton.titleLabel.text = nil; + } NSString *btnTitle = NSLocalizedStringFromTable(@"settings_report_bug", @"Vector", nil); [reportBugBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal]; @@ -1965,11 +1993,23 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); } else if (section == SETTINGS_SECTION_LABS_INDEX) { -#ifdef USE_JITSI_WIDGET if (row == LABS_MATRIX_APPS_INDEX) { MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; + labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_labs_matrix_apps", @"Vector", nil); + labelAndSwitchCell.mxkSwitch.on = [[NSUserDefaults standardUserDefaults] boolForKey:@"matrixApps"]; + + [labelAndSwitchCell.mxkSwitch removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside]; + [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleLabsMatrixApps:) forControlEvents:UIControlEventTouchUpInside]; + + cell = labelAndSwitchCell; + } +#ifdef USE_JITSI_WIDGET + else if (row == LABS_USE_JITSI_WIDGET_INDEX) + { + MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; + labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_labs_create_conference_with_jitsi", @"Vector", nil); labelAndSwitchCell.mxkSwitch.on = [[NSUserDefaults standardUserDefaults] boolForKey:@"createConferenceCallsWithJitsi"]; @@ -2052,6 +2092,11 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); { exportKeysBtnCell = [[MXKTableViewCellWithButton alloc] init]; } + else + { + // Fix https://github.com/vector-im/riot-ios/issues/1354 + exportKeysBtnCell.mxkButton.titleLabel.text = nil; + } NSString *btnTitle = NSLocalizedStringFromTable(@"settings_crypto_export", @"Vector", nil); [exportKeysBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal]; @@ -2747,6 +2792,19 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); } } +- (void)toggleLabsMatrixApps:(id)sender +{ + if (sender && [sender isKindOfClass:UISwitch.class]) + { + UISwitch *switchButton = (UISwitch*)sender; + + [[NSUserDefaults standardUserDefaults] setBool:switchButton.isOn forKey:@"matrixApps"]; + [[NSUserDefaults standardUserDefaults] synchronize]; + + [self.tableView reloadData]; + } +} + - (void)toggleJitsiForConference:(id)sender { if (sender && [sender isKindOfClass:UISwitch.class]) diff --git a/Riot/ViewController/StartChatViewController.m b/Riot/ViewController/StartChatViewController.m index 8944c37af..459f62ec0 100644 --- a/Riot/ViewController/StartChatViewController.m +++ b/Riot/ViewController/StartChatViewController.m @@ -148,7 +148,7 @@ [self refreshSearchBarItemsColor:_searchBarView]; - _searchBarHeaderBorder.backgroundColor = kRiotColorSilver; + _searchBarHeaderBorder.backgroundColor = kRiotAuxiliaryColor; // Check the table view style to select its bg color. self.contactsTableView.backgroundColor = ((self.contactsTableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); diff --git a/Riot/ViewController/UnifiedSearchViewController.m b/Riot/ViewController/UnifiedSearchViewController.m index e1b9e5d4c..560a15bc5 100644 --- a/Riot/ViewController/UnifiedSearchViewController.m +++ b/Riot/ViewController/UnifiedSearchViewController.m @@ -95,7 +95,7 @@ [super viewDidLoad]; - // Add the Vector background image when search bar is empty + // Add the Riot background image when search bar is empty [self addBackgroundImageViewToView:self.view]; // Initialize here the data sources if a matrix session has been already set. @@ -107,6 +107,18 @@ [super showSearch:NO]; } +- (void)userInterfaceThemeDidChange +{ + [super userInterfaceThemeDidChange]; + + UIImageView *backgroundImageView = self.backgroundImageView; + if (backgroundImageView) + { + UIImage *image = [MXKTools paintImage:backgroundImageView.image withColor:kRiotKeyboardColor]; + backgroundImageView.image = image; + } +} + - (void)destroy { [super destroy]; diff --git a/Riot/ViewController/UsersDevicesViewController.m b/Riot/ViewController/UsersDevicesViewController.m index dc88de02b..a92d779b6 100644 --- a/Riot/ViewController/UsersDevicesViewController.m +++ b/Riot/ViewController/UsersDevicesViewController.m @@ -83,6 +83,7 @@ { self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; // Check the table view style to select its bg color. self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? kRiotPrimaryBgColor : kRiotSecondaryBgColor); diff --git a/Riot/ViewController/WebViewViewController.m b/Riot/ViewController/WebViewViewController.m index 88536d9f6..67c5e69e5 100644 --- a/Riot/ViewController/WebViewViewController.m +++ b/Riot/ViewController/WebViewViewController.m @@ -54,6 +54,7 @@ self.view.backgroundColor = kRiotPrimaryBgColor; self.defaultBarTintColor = kRiotSecondaryBgColor; self.barTitleColor = kRiotPrimaryTextColor; + self.activityIndicator.backgroundColor = kRiotOverlayColor; webView.backgroundColor = kRiotPrimaryBgColor; } diff --git a/Riot/ViewController/Widgets/IntegrationManagerViewController.h b/Riot/ViewController/Widgets/IntegrationManagerViewController.h new file mode 100644 index 000000000..213147720 --- /dev/null +++ b/Riot/ViewController/Widgets/IntegrationManagerViewController.h @@ -0,0 +1,40 @@ +/* + Copyright 2017 Vector Creations Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "WebViewViewController.h" + +#import + +FOUNDATION_EXPORT NSString *const kIntegrationManagerMainScreen; +FOUNDATION_EXPORT NSString *const kIntegrationManagerAddIntegrationScreen; + +/** + `IntegrationManagerViewController` displays the Modular integration manager webapp + into a webview. + */ +@interface IntegrationManagerViewController : WebViewViewController + +/** + Initialise with params for the Modular interface webapp. + + @param mxSession the session to use. + @param roomId the room where to set up widgets. + @param screen the screen to display in the Modular interface webapp. Can be nil. + @param widgetId the id of the widget in case of widget configuration edition. Can be nil. + */ +- (instancetype)initForMXSession:(MXSession*)mxSession inRoom:(NSString*)roomId screen:(NSString*)screen widgetId:(NSString*)widgetId; + +@end diff --git a/Riot/ViewController/Widgets/IntegrationManagerViewController.m b/Riot/ViewController/Widgets/IntegrationManagerViewController.m new file mode 100644 index 000000000..6add0083c --- /dev/null +++ b/Riot/ViewController/Widgets/IntegrationManagerViewController.m @@ -0,0 +1,741 @@ +/* + 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 "IntegrationManagerViewController.h" + +#import "WidgetManager.h" + +#import + +NSString *const kIntegrationManagerMainScreen = nil; +NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ"; + +NSString *const kJavascriptSendResponseToModular = @"riotIOS.sendResponse('%@', %@);"; + + +@interface IntegrationManagerViewController () +{ + MXSession *mxSession; + NSString *roomId; + NSString *screen; + NSString *widgetId; + NSString *scalarToken; + + MXHTTPOperation *operation; +} + +@end + +@implementation IntegrationManagerViewController + +- (instancetype)initForMXSession:(MXSession *)theMXSession inRoom:(NSString *)theRoomId screen:(NSString *)theScreen widgetId:(NSString *)theWidgetId +{ + self = [super init]; + if (self) + { + mxSession = theMXSession; + roomId = theRoomId; + screen = theScreen; + widgetId = theWidgetId; + } + return self; +} + +- (void)destroy +{ + [super destroy]; + + [operation cancel]; + operation = nil; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + webView.scalesPageToFit = NO; + webView.scrollView.bounces = NO; + + webView.delegate = self; +} + +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + if (!self.URL && !operation) + { + __weak __typeof__(self) weakSelf = self; + + [self startActivityIndicator]; + + // Make sure we a scalar token + operation = [[WidgetManager sharedManager] getScalarTokenForMXSession:mxSession success:^(NSString *theScalarToken) { + + typeof(self) self = weakSelf; + + if (self) + { + self->operation = nil; + [self stopActivityIndicator]; + + scalarToken = theScalarToken; + + // Launch the webview on the right modular webapp page + self.URL = [self interfaceUrl]; + } + + } failure:^(NSError *error) { + + typeof(self) self = weakSelf; + + if (self) + { + self->operation = nil; + [self stopActivityIndicator]; + } + }]; + } +} + +#pragma mark - Private methods + +/** + Build the URL to use in the Modular interface webapp. + */ +- (NSString *)interfaceUrl +{ + NSMutableString *url; + + if (scalarToken) + { + url = [NSMutableString stringWithFormat:@"%@?scalar_token=%@&room_id=%@", + [[NSUserDefaults standardUserDefaults] objectForKey:@"integrationsUiUrl"], + [scalarToken stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], + [roomId stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] + ]; + + if (screen) + { + [url appendString:@"&screen="]; + [url appendString:[screen stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + } + + if (widgetId) + { + [url appendString:@"&integ_id="]; + [url appendString:[widgetId stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + } + } + + return url; +} + +- (void)enableDebug +{ + // 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;"]; +} + +#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]; +} + +- (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; + } + return YES; +} + +#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"]); + + if ([action isEqualToString:@"close_scalar"]) + { + [self withdrawViewControllerAnimated:YES completion:nil]; + return; + } + + if (!roomIdInEvent) + { + [self sendLocalisedError:@"widget_integration_missing_room_id" toEvent:eventData]; + return; + } + + if (![roomIdInEvent isEqualToString:roomId]) + { + [self sendError:[NSString stringWithFormat:NSLocalizedStringFromTable(@"widget_integration_room_not_visible", @"Vector", nil), roomIdInEvent] toEvent:eventData]; + return; + } + + + // These APIs don't require userId + if ([@"join_rules_state" isEqualToString:action]) + { + [self getJoinRules:eventData]; + return; + } + else if ([@"set_plumbing_state" isEqualToString:action]) + { + [self setPlumbingState:eventData]; + return; + } + else if ([@"get_membership_count" isEqualToString:action]) + { + [self getMembershipCount:eventData]; + return; + } + else if ([@"set_widget" isEqualToString:action]) + { + [self setWidget:eventData]; + return; + } + else if ([@"get_widgets" isEqualToString:action]) + { + [self getWidgets:eventData]; + return; + } + else if ([@"can_send_event" isEqualToString:action]) + { + [self canSendEvent:eventData]; + return; + } + + + if (!userId) + { + [self sendLocalisedError:@"widget_integration_missing_user_id" toEvent:eventData]; + return; + } + + if ([@"membership_state" isEqualToString:action]) + { + [self getMembershipState:userId eventData:eventData]; + } + else if ([@"invite" isEqualToString:action]) + { + [self inviteUser:userId eventData:eventData]; + } + else if ([@"bot_options" isEqualToString:action]) + { + [self getBotOptions:userId eventData:eventData]; + } + else if ([@"set_bot_options" isEqualToString:action]) + { + [self setBotOptions:userId eventData:eventData]; + } + else if ([@"set_bot_power" isEqualToString:action]) + { + [self setBotPower:userId eventData:eventData]; + } + else + { + NSLog(@"[IntegrationManagerViewControllerVC] Unhandled postMessage event with action %@: %@", action, JSData); + } +} + +- (void)sendBoolResponse:(BOOL)response toEvent:(NSDictionary*)eventData +{ + // Convert BOOL to "true" or "false" + NSString *js = [NSString stringWithFormat:kJavascriptSendResponseToModular, + eventData[@"_id"], + response ? @"true" : @"false"]; + + [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 *room = [mxSession roomWithRoomId:roomId]; + if (!room) + { + [self sendLocalisedError:@"widget_integration_room_not_recognised" toEvent:eventData]; + } + + return room; +} + +- (void)inviteUser:(NSString*)userId eventData:(NSDictionary*)eventData +{ + NSLog(@"[IntegrationManagerVC] Received request to invite %@ into room %@.", userId, roomId); + + MXRoom *room = [self roomCheckWithEvent:eventData]; + + if (room) + { + MXRoomMember *member = [room.state memberWithUserId:userId]; + if (member && member.membership == MXMembershipJoin) + { + [self sendNSObjectResponse:@{ + @"success": @(YES) + } + toEvent:eventData]; + } + else + { + __weak __typeof__(self) weakSelf = self; + + [room inviteUser:userId success:^{ + + typeof(self) self = weakSelf; + if (self) + { + [self sendNSObjectResponse:@{ + @"success": @(YES) + } + toEvent:eventData]; + } + + } failure:^(NSError *error) { + + typeof(self) self = weakSelf; + if (self) + { + [self sendLocalisedError:@"widget_integration_need_to_be_able_to_invite" toEvent:eventData]; + } + }]; + } + } +} + +- (void)setWidget:(NSDictionary*)eventData +{ + NSLog(@"[IntegrationManagerVC] Received request to set widget in room %@.", roomId); + + MXRoom *room = [self roomCheckWithEvent:eventData]; + + if (room) + { + NSString *widget_id, *widgetType, *widgetUrl; + 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"]); + + if (!widget_id) + { + [self sendLocalisedError:@"widget_integration_unable_to_create" toEvent:eventData]; // new Error("Missing required widget fields.")); + return; + } + + NSMutableDictionary *widgetEventContent = [NSMutableDictionary dictionary]; + if (widgetUrl) + { + if (!widgetType) + { + [self sendLocalisedError:@"widget_integration_unable_to_create" toEvent:eventData]; + return; + } + + widgetEventContent[@"type"] = widgetType; + widgetEventContent[@"url"] = widgetUrl; + + if (widgetName) + { + widgetEventContent[@"name"] = widgetName; + } + if (widgetData) + { + widgetEventContent[@"data"] = widgetData; + } + } + + __weak __typeof__(self) weakSelf = self; + + [room sendStateEventOfType:kWidgetEventTypeString + content:widgetEventContent + stateKey:widget_id + success:^(NSString *eventId) { + + typeof(self) self = weakSelf; + if (self) + { + [self sendNSObjectResponse:@{ + @"success": @(YES) + } + toEvent:eventData]; + } + } + failure:^(NSError *error) { + + typeof(self) self = weakSelf; + if (self) + { + [self sendLocalisedError:@"widget_integration_failed_to_send_request" toEvent:eventData]; + } + }]; + } +} + +- (void)getWidgets:(NSDictionary*)eventData +{ + MXRoom *room = [self roomCheckWithEvent:eventData]; + + if (room) + { + NSArray *widgets = [[WidgetManager sharedManager] widgetsInRoom:room]; + + NSMutableArray *widgetStateEvents = [NSMutableArray arrayWithCapacity:widgets.count]; + + for (Widget *widget in widgets) + { + [widgetStateEvents addObject:widget.widgetEvent.JSONDictionary]; + } + + [self sendNSObjectResponse:widgetStateEvents toEvent:eventData]; + } +} + +- (void)canSendEvent:(NSDictionary*)eventData +{ + NSString *eventType; + BOOL isState = NO; + + MXRoom *room = [self roomCheckWithEvent:eventData]; + + if (room) + { + if (room.state.membership != MXMembershipJoin) + { + [self sendLocalisedError:@"widget_integration_must_be_in_room" toEvent:eventData]; + return; + } + + MXJSONModelSetString(eventType, eventData[@"event_type"]); + MXJSONModelSetBoolean(isState, eventData[@"is_state"]); + + MXRoomPowerLevels *powerLevels = room.state.powerLevels; + NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:mxSession.myUser.userId]; + + BOOL canSend = NO; + + if (isState) + { + canSend = (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:eventType]); + } + else + { + canSend = (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsMessage:eventType]); + } + + if (canSend) + { + [self sendBoolResponse:YES toEvent:eventData]; + } + else + { + [self sendLocalisedError:@"widget_integration_no_permission_in_room" toEvent:eventData]; + } + } +} + +- (void)getMembershipState:(NSString*)userId eventData:(NSDictionary*)eventData +{ + NSLog(@"[IntegrationManagerVC] membership_state of %@ in room %@ requested.", userId, roomId); + + MXRoom *room = [self roomCheckWithEvent:eventData]; + if (room) + { + MXRoomMember *member = [room.state memberWithUserId:userId]; + [self sendNSObjectResponse:member.originalEvent.content toEvent:eventData]; + } +} + +- (void)getJoinRules:(NSDictionary*)eventData +{ + NSLog(@"[IntegrationManagerVC] join_rules of %@ requested.", roomId); + + MXRoom *room = [self roomCheckWithEvent:eventData]; + if (room) + { + MXEvent *event = [room.state stateEventsWithType:kMXEventTypeStringRoomJoinRules].lastObject; + [self sendNSObjectResponse:event.JSONDictionary toEvent:eventData]; + } +} + +- (void)setPlumbingState:(NSDictionary*)eventData +{ + NSLog(@"[IntegrationManagerVC] Received request to set plumbing state to status %@ in room %@.", eventData[@"status"], roomId); + + MXRoom *room = [self roomCheckWithEvent:eventData]; + if (room) + { + NSString *status; + MXJSONModelSetString(status, eventData[@"status"]); + + if (status) + { + __weak __typeof__(self) weakSelf = self; + + [room sendStateEventOfType:kMXEventTypeStringRoomPlumbing + content:@{ + @"status": status + } + stateKey:nil + success:^(NSString *eventId) { + + typeof(self) self = weakSelf; + if (self) + { + [self sendNSObjectResponse:@{ + @"success": @(YES) + } + toEvent:eventData]; + } + } + failure:^(NSError *error) { + + typeof(self) self = weakSelf; + if (self) + { + [self sendLocalisedError:@"widget_integration_failed_to_send_request" toEvent:eventData]; + } + }]; + } + else + { + NSLog(@"[IntegrationManagerVC] setPlumbingState. Error: Plumbing state status should be a string."); + } + } +} + +- (void)getBotOptions:(NSString*)userId eventData:(NSDictionary*)eventData +{ + NSLog(@"[IntegrationManagerVC] Received request to get options for bot %@ in room %@", userId, roomId); + + MXRoom *room = [self roomCheckWithEvent:eventData]; + if (room) + { + NSString *stateKey = [NSString stringWithFormat:@"_%@", userId]; + + NSArray *stateEvents = [room.state stateEventsWithType:kMXEventTypeStringRoomBotOptions]; + + MXEvent *botOptionsEvent; + + for (MXEvent *stateEvent in stateEvents) + { + if ([stateEvent.stateKey isEqualToString:stateKey]) + { + if (!botOptionsEvent || stateEvent.ageLocalTs > botOptionsEvent.ageLocalTs) + { + botOptionsEvent = stateEvent; + } + } + } + + [self sendNSObjectResponse:botOptionsEvent.JSONDictionary toEvent:eventData]; + } +} + +- (void)setBotOptions:(NSString*)userId eventData:(NSDictionary*)eventData +{ + NSLog(@"[IntegrationManagerVC] Received request to set options for bot %@ in room %@", userId, roomId); + + MXRoom *room = [self roomCheckWithEvent:eventData]; + if (room) + { + NSDictionary *content; + MXJSONModelSetDictionary(content, eventData[@"content"]); + + if (content) + { + __weak __typeof__(self) weakSelf = self; + + NSString *stateKey = [NSString stringWithFormat:@"_%@", userId]; + + [room sendStateEventOfType:kMXEventTypeStringRoomBotOptions + content:content + stateKey:stateKey + success:^(NSString *eventId) { + + typeof(self) self = weakSelf; + if (self) + { + [self sendNSObjectResponse:@{ + @"success": @(YES) + } + toEvent:eventData]; + } + } + failure:^(NSError *error) { + + typeof(self) self = weakSelf; + if (self) + { + [self sendLocalisedError:@"widget_integration_failed_to_send_request" toEvent:eventData]; + } + }]; + } + else + { + NSLog(@"[IntegrationManagerVC] setBotOptions. Error: options should be a dict."); + } + } +} + +- (void)setBotPower:(NSString*)userId eventData:(NSDictionary*)eventData +{ + NSLog(@"[IntegrationManagerVC] Received request to set power level to %@ for bot %@ in room %@.", eventData[@"level"], userId, roomId); + + MXRoom *room = [self roomCheckWithEvent:eventData]; + if (room) + { + NSInteger level = -1; + MXJSONModelSetInteger(level, eventData[@"level"]); + + if (level >= 0) + { + __weak __typeof__(self) weakSelf = self; + + [room setPowerLevelOfUserWithUserID:userId powerLevel:level success:^{ + + typeof(self) self = weakSelf; + if (self) + { + [self sendNSObjectResponse:@{ + @"success": @(YES) + } + toEvent:eventData]; + } + + } failure:^(NSError *error) { + + typeof(self) self = weakSelf; + if (self) + { + [self sendLocalisedError:@"widget_integration_failed_to_send_request" toEvent:eventData]; + } + }]; + } + else + { + NSLog(@"[IntegrationManagerVC] setBotPower. Power level must be positive integer."); + [self sendLocalisedError:@"widget_integration_positive_power_level" toEvent:eventData]; + } + } +} + +- (void)getMembershipCount:(NSDictionary*)eventData +{ + MXRoom *room = [self roomCheckWithEvent:eventData]; + if (room) + { + NSUInteger membershipCount = room.state.joinedMembers.count; + [self sendIntegerResponse:membershipCount toEvent:eventData]; + } +} + +@end diff --git a/Riot/ViewController/Widgets/WidgetViewController.h b/Riot/ViewController/Widgets/WidgetViewController.h new file mode 100644 index 000000000..eb439a3ac --- /dev/null +++ b/Riot/ViewController/Widgets/WidgetViewController.h @@ -0,0 +1,33 @@ +/* + Copyright 2017 Vector Creations Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "WebViewViewController.h" + +#import "WidgetManager.h" + +/** + `WidgetViewController` displays widget within a webview. + */ +@interface WidgetViewController : WebViewViewController + +/** + Init 'WidgetViewController' instance with a widget. + + @param widget the widget to open. + */ +- (instancetype)initForWidget:(Widget*)widget; + +@end diff --git a/Riot/ViewController/Widgets/WidgetViewController.m b/Riot/ViewController/Widgets/WidgetViewController.m new file mode 100644 index 000000000..355306fac --- /dev/null +++ b/Riot/ViewController/Widgets/WidgetViewController.m @@ -0,0 +1,48 @@ +/* + Copyright 2017 Vector Creations Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "WidgetViewController.h" + +@interface WidgetViewController () +{ + Widget *widget; +} + +@end + +@implementation WidgetViewController + +- (instancetype)initForWidget:(Widget*)theWidget +{ + self = [super initWithURL:theWidget.url]; + if (self) + { + widget = theWidget; + } + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + webView.scalesPageToFit = NO; + webView.scrollView.bounces = NO; + + self.navigationItem.title = widget.name; +} + +@end diff --git a/Riot/Views/RoomInputToolbar/RoomInputToolbarView.m b/Riot/Views/RoomInputToolbar/RoomInputToolbarView.m index 58cc588c0..bf178b2ca 100644 --- a/Riot/Views/RoomInputToolbar/RoomInputToolbarView.m +++ b/Riot/Views/RoomInputToolbar/RoomInputToolbarView.m @@ -82,7 +82,7 @@ // Remove default toolbar background color self.backgroundColor = [UIColor clearColor]; - self.separatorView.backgroundColor = kRiotColorSilver; + self.separatorView.backgroundColor = kRiotAuxiliaryColor; // Custom the growingTextView display growingTextView.layer.cornerRadius = 0; diff --git a/Riot/Views/RoomList/RecentTableViewCell.m b/Riot/Views/RoomList/RecentTableViewCell.m index ac16519d2..b0bf69a75 100644 --- a/Riot/Views/RoomList/RecentTableViewCell.m +++ b/Riot/Views/RoomList/RecentTableViewCell.m @@ -23,7 +23,7 @@ #import "RiotDesignValues.h" -#import "MXRoom+Riot.h" +#import "MXRoomSummary+Riot.h" @implementation RecentTableViewCell @@ -111,7 +111,7 @@ } else { - self.missedNotifAndUnreadIndicator.backgroundColor = kRiotColorSilver; + self.missedNotifAndUnreadIndicator.backgroundColor = kRiotAuxiliaryColor; } // Use bold font for the room title @@ -143,7 +143,7 @@ self.encryptedRoomIcon.hidden = !roomCellData.roomSummary.isEncrypted; - [roomCellData.roomSummary.room setRoomAvatarImageIn:self.roomAvatar]; + [roomCellData.roomSummary setRoomAvatarImageIn:self.roomAvatar]; } else { diff --git a/Riot/Views/RoomList/RoomCollectionViewCell.m b/Riot/Views/RoomList/RoomCollectionViewCell.m index b47a2a8cd..e5492ac38 100644 --- a/Riot/Views/RoomList/RoomCollectionViewCell.m +++ b/Riot/Views/RoomList/RoomCollectionViewCell.m @@ -20,7 +20,7 @@ #import "RiotDesignValues.h" -#import "MXRoom+Riot.h" +#import "MXRoomSummary+Riot.h" #import "MXTools.h" @@ -171,7 +171,7 @@ self.encryptedRoomIcon.hidden = !roomCellData.roomSummary.isEncrypted; - [roomCellData.roomSummary.room setRoomAvatarImageIn:self.roomAvatar]; + [roomCellData.roomSummary setRoomAvatarImageIn:self.roomAvatar]; } } diff --git a/Riot/Views/RoomList/RoomTableViewCell.m b/Riot/Views/RoomList/RoomTableViewCell.m index 938038c77..4f33d2821 100644 --- a/Riot/Views/RoomList/RoomTableViewCell.m +++ b/Riot/Views/RoomList/RoomTableViewCell.m @@ -18,7 +18,7 @@ #import "RoomTableViewCell.h" #import "RiotDesignValues.h" -#import "MXRoom+Riot.h" +#import "MXRoomSummary+Riot.h" @implementation RoomTableViewCell @@ -50,9 +50,9 @@ - (void)render:(MXRoom *)room { - [room setRoomAvatarImageIn:self.avatarImageView]; + [room.summary setRoomAvatarImageIn:self.avatarImageView]; - self.titleLabel.text = room.riotDisplayname; + self.titleLabel.text = room.summary.displayname; self.directRoomBorderView.hidden = !room.isDirect; diff --git a/Riot/Views/RoomTitle/ExpandedRoomTitleView.m b/Riot/Views/RoomTitle/ExpandedRoomTitleView.m index de0048a37..90ce7613b 100644 --- a/Riot/Views/RoomTitle/ExpandedRoomTitleView.m +++ b/Riot/Views/RoomTitle/ExpandedRoomTitleView.m @@ -19,8 +19,6 @@ #import "RiotDesignValues.h" -#import "MXRoom+Riot.h" - @implementation ExpandedRoomTitleView + (UINib *)nib @@ -38,7 +36,7 @@ { [super customizeViewRendering]; - self.displayNameTextField.textColor = (self.mxRoom.riotDisplayname.length ? kRiotPrimaryTextColor : kRiotSecondaryTextColor); + self.displayNameTextField.textColor = (self.mxRoom.summary.displayname.length ? kRiotPrimaryTextColor : kRiotSecondaryTextColor); self.roomTopic.textColor = kRiotTopicTextColor; self.roomMembers.textColor = kRiotColorGreen; } @@ -49,7 +47,7 @@ if (self.mxRoom) { - self.displayNameTextField.text = self.mxRoom.riotDisplayname; + self.displayNameTextField.text = self.mxRoom.summary.displayname; if (!self.displayNameTextField.text.length) { self.displayNameTextField.text = NSLocalizedStringFromTable(@"room_displayname_no_title", @"Vector", nil); diff --git a/Riot/Views/RoomTitle/PreviewRoomTitleView.m b/Riot/Views/RoomTitle/PreviewRoomTitleView.m index 53ab2b615..c41a2da1e 100644 --- a/Riot/Views/RoomTitle/PreviewRoomTitleView.m +++ b/Riot/Views/RoomTitle/PreviewRoomTitleView.m @@ -19,8 +19,6 @@ #import "RiotDesignValues.h" -#import "MXRoom+Riot.h" - @implementation PreviewRoomTitleView + (UINib *)nib @@ -64,7 +62,7 @@ self.backgroundColor = kRiotPrimaryBgColor; self.mainHeaderBackground.backgroundColor = kRiotSecondaryBgColor; - self.displayNameTextField.textColor = (self.mxRoom.riotDisplayname.length ? kRiotPrimaryTextColor : kRiotSecondaryTextColor); + self.displayNameTextField.textColor = (self.mxRoom.summary.displayname.length ? kRiotPrimaryTextColor : kRiotSecondaryTextColor); self.roomTopic.textColor = kRiotTopicTextColor; self.roomTopic.numberOfLines = 0; @@ -145,7 +143,7 @@ else if (self.mxRoom) { // The user is here invited to join a room (This invitation has been received from server sync) - self.displayNameTextField.text = self.mxRoom.riotDisplayname; + self.displayNameTextField.text = self.mxRoom.summary.displayname; if (!self.displayNameTextField.text.length) { self.displayNameTextField.text = NSLocalizedStringFromTable(@"room_displayname_no_title", @"Vector", nil); diff --git a/Riot/Views/RoomTitle/RoomAvatarTitleView.m b/Riot/Views/RoomTitle/RoomAvatarTitleView.m index b1cd35495..a9a6bb2ea 100644 --- a/Riot/Views/RoomTitle/RoomAvatarTitleView.m +++ b/Riot/Views/RoomTitle/RoomAvatarTitleView.m @@ -19,7 +19,7 @@ #import "RiotDesignValues.h" -#import "MXRoom+Riot.h" +#import "MXRoomSummary+Riot.h" @implementation RoomAvatarTitleView @@ -71,7 +71,7 @@ if (self.mxRoom) { - [self.mxRoom setRoomAvatarImageIn:self.roomAvatar]; + [self.mxRoom.summary setRoomAvatarImageIn:self.roomAvatar]; } else if (self.roomAvatarURL) { diff --git a/Riot/Views/RoomTitle/RoomTitleView.m b/Riot/Views/RoomTitle/RoomTitleView.m index 6010a3b86..2b856af2c 100644 --- a/Riot/Views/RoomTitle/RoomTitleView.m +++ b/Riot/Views/RoomTitle/RoomTitleView.m @@ -19,8 +19,6 @@ #import "RiotDesignValues.h" -#import "MXRoom+Riot.h" - @implementation RoomTitleView + (UINib *)nib @@ -111,7 +109,7 @@ { [super customizeViewRendering]; - self.displayNameTextField.textColor = (self.mxRoom.riotDisplayname.length ? kRiotPrimaryTextColor : kRiotSecondaryTextColor); + self.displayNameTextField.textColor = (self.mxRoom.summary.displayname.length ? kRiotPrimaryTextColor : kRiotSecondaryTextColor); } - (void)setRoomPreviewData:(RoomPreviewData *)roomPreviewData @@ -132,7 +130,7 @@ } else if (self.mxRoom) { - self.displayNameTextField.text = self.mxRoom.riotDisplayname; + self.displayNameTextField.text = self.mxRoom.summary.displayname; if (!self.displayNameTextField.text.length) { self.displayNameTextField.text = NSLocalizedStringFromTable(@"room_displayname_no_title", @"Vector", nil); diff --git a/Riot/Views/RoomTitle/SimpleRoomTitleView.m b/Riot/Views/RoomTitle/SimpleRoomTitleView.m index a18bf94f3..982784428 100644 --- a/Riot/Views/RoomTitle/SimpleRoomTitleView.m +++ b/Riot/Views/RoomTitle/SimpleRoomTitleView.m @@ -19,8 +19,6 @@ #import "RiotDesignValues.h" -#import "MXRoom+Riot.h" - @implementation SimpleRoomTitleView + (UINib *)nib @@ -70,7 +68,7 @@ { [super customizeViewRendering]; - self.displayNameTextField.textColor = (self.mxRoom.riotDisplayname.length ? kRiotPrimaryTextColor : kRiotSecondaryTextColor); + self.displayNameTextField.textColor = (self.mxRoom.summary.displayname.length ? kRiotPrimaryTextColor : kRiotSecondaryTextColor); } - (void)refreshDisplay @@ -79,7 +77,7 @@ if (self.mxRoom) { - self.displayNameTextField.text = self.mxRoom.riotDisplayname; + self.displayNameTextField.text = self.mxRoom.summary.displayname; if (!self.displayNameTextField.text.length) { self.displayNameTextField.text = NSLocalizedStringFromTable(@"room_displayname_no_title", @"Vector", nil); diff --git a/Riot/Views/Search/MessagesSearchResultAttachmentBubbleCell.m b/Riot/Views/Search/MessagesSearchResultAttachmentBubbleCell.m index be6a4424c..4968f2496 100644 --- a/Riot/Views/Search/MessagesSearchResultAttachmentBubbleCell.m +++ b/Riot/Views/Search/MessagesSearchResultAttachmentBubbleCell.m @@ -19,8 +19,6 @@ #import "RiotDesignValues.h" -#import "MXRoom+Riot.h" - @implementation MessagesSearchResultAttachmentBubbleCell - (void)customizeTableViewCellRendering @@ -43,7 +41,7 @@ MXRoom* room = [bubbleData.mxSession roomWithRoomId:bubbleData.roomId]; if (room) { - self.roomNameLabel.text = room.riotDisplayname; + self.roomNameLabel.text = room.summary.displayname; if (!self.roomNameLabel.text.length) { self.roomNameLabel.text = NSLocalizedStringFromTable(@"room_displayname_no_title", @"Vector", nil); diff --git a/Riot/Views/Search/MessagesSearchResultTextMsgBubbleCell.m b/Riot/Views/Search/MessagesSearchResultTextMsgBubbleCell.m index 364e93cde..8359cc26e 100644 --- a/Riot/Views/Search/MessagesSearchResultTextMsgBubbleCell.m +++ b/Riot/Views/Search/MessagesSearchResultTextMsgBubbleCell.m @@ -19,8 +19,6 @@ #import "RiotDesignValues.h" -#import "MXRoom+Riot.h" - @implementation MessagesSearchResultTextMsgBubbleCell - (void)customizeTableViewCellRendering @@ -43,7 +41,7 @@ MXRoom* room = [bubbleData.mxSession roomWithRoomId:bubbleData.roomId]; if (room) { - self.roomNameLabel.text = room.riotDisplayname; + self.roomNameLabel.text = room.summary.displayname; } else { diff --git a/RiotShareExtension/ViewController/RoomsListViewController.m b/RiotShareExtension/ViewController/RoomsListViewController.m index 709277a09..7322674d5 100644 --- a/RiotShareExtension/ViewController/RoomsListViewController.m +++ b/RiotShareExtension/ViewController/RoomsListViewController.m @@ -130,7 +130,8 @@ - (void)showShareAlertForRoomPath:(NSIndexPath *)indexPath { - NSString *receipantName = [self.dataSource getRoomAtIndexPath:indexPath].riotDisplayname; + // @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) { receipantName = NSLocalizedStringFromTable(@"room_displayname_no_title", @"Vector", nil);