diff --git a/CHANGES.rst b/CHANGES.rst index b3f7cd088..c3ff551ec 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changes to be released in next version * 🙌 Improvements + * Jitsi: Use Jitsi server from homeserver's Well Known, if present, to create conferences (#3158). + * RoomMemberDetailsVC: Enable / disable "Hide all messages from this user" from settings (#4281). + * RoomVC: Show / Hide More and Report Content contextual menu from settings (#4285). + * SettingsVC: Show / hide NSFW and decrypted content options from build settings (#4290). + * RoomVC: Tweaked Scroll to Bottom FAB button (#4272). + * DesignKit: Introduce a new framework to manage design components. * Add Jitsi widget remove banner for privileged users. * Update "Jump to unread" banner to a pill style button. diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index 0df5a4c8b..e7d771d1f 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -180,7 +180,7 @@ final class BuildSettings: NSObject { "https://scalar-staging.riot.im/scalar/api", ] // Jitsi server used outside integrations to create conference calls from the call button in the timeline - static let jitsiServerUrl = NSURL(string: "https://jitsi.riot.im") + static let jitsiServerUrl: URL = URL(string: "https://jitsi.riot.im")! // MARK: - Features @@ -242,6 +242,8 @@ final class BuildSettings: NSObject { static let settingsScreenShowChangePassword:Bool = true static let settingsScreenShowInviteFriends:Bool = true static let settingsScreenShowEnableStunServerFallback: Bool = true + static let settingsScreenShowNotificationDecodedContentOption: Bool = true + static let settingsScreenShowNsfwRoomsOption: Bool = true static let settingsSecurityScreenShowSessions:Bool = true static let settingsSecurityScreenShowSetupBackup:Bool = true static let settingsSecurityScreenShowRestoreBackup:Bool = true @@ -268,6 +270,12 @@ final class BuildSettings: NSObject { static let roomScreenAllowMediaLibraryAction: Bool = true static let roomScreenAllowStickerAction: Bool = true static let roomScreenAllowFilesAction: Bool = true + + // MARK: - Room Contextual Menu + + static let roomContextualMenuShowMoreOptionForMessages: Bool = true + static let roomContextualMenuShowMoreOptionForStates: Bool = true + static let roomContextualMenuShowReportContentOption: Bool = true // MARK: - Room Info Screen @@ -284,6 +292,10 @@ final class BuildSettings: NSObject { static let roomSettingsScreenShowAdvancedSettings: Bool = true static let roomSettingsScreenAdvancedShowEncryptToVerifiedOption: Bool = true + // MARK: - Room Member Screen + + static let roomMemberScreenShowIgnore: Bool = true + // MARK: - Message static let messageDetailsAllowShare: Bool = true static let messageDetailsAllowPermalink: Bool = true diff --git a/DesignKit/Common.xcconfig b/DesignKit/Common.xcconfig new file mode 100644 index 000000000..40cf7124b --- /dev/null +++ b/DesignKit/Common.xcconfig @@ -0,0 +1,27 @@ +// +// Copyright 2021 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. +// + +// Configuration settings file format documentation can be found at: +// https://help.apple.com/xcode/#/dev745c5c974 + +#include "Config/AppIdentifiers.xcconfig" + +PRODUCT_NAME = DesignKit +PRODUCT_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER).designkit + +INFOPLIST_FILE = DesignKit/Info.plist + +SKIP_INSTALL = YES diff --git a/DesignKit/Debug.xcconfig b/DesignKit/Debug.xcconfig new file mode 100644 index 000000000..11a7288a4 --- /dev/null +++ b/DesignKit/Debug.xcconfig @@ -0,0 +1,20 @@ +// +// Copyright 2021 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. +// + +// Configuration settings file format documentation can be found at: +// https://help.apple.com/xcode/#/dev745c5c974 + +#include "Common.xcconfig" diff --git a/DesignKit/DesignKit.h b/DesignKit/DesignKit.h new file mode 100644 index 000000000..4ff68e722 --- /dev/null +++ b/DesignKit/DesignKit.h @@ -0,0 +1,27 @@ +// +// Copyright 2021 New Vector 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 + +//! Project version number for DesignKit. +FOUNDATION_EXPORT double DesignKitVersionNumber; + +//! Project version string for DesignKit. +FOUNDATION_EXPORT const unsigned char DesignKitVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/DesignKit/Info.plist b/DesignKit/Info.plist new file mode 100644 index 000000000..c0701c6d7 --- /dev/null +++ b/DesignKit/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/DesignKit/Release.xcconfig b/DesignKit/Release.xcconfig new file mode 100644 index 000000000..11a7288a4 --- /dev/null +++ b/DesignKit/Release.xcconfig @@ -0,0 +1,20 @@ +// +// Copyright 2021 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. +// + +// Configuration settings file format documentation can be found at: +// https://help.apple.com/xcode/#/dev745c5c974 + +#include "Common.xcconfig" diff --git a/DesignKit/Source/Colors.swift b/DesignKit/Source/Colors.swift new file mode 100644 index 000000000..b5dc66261 --- /dev/null +++ b/DesignKit/Source/Colors.swift @@ -0,0 +1,64 @@ +// +// Copyright 2021 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import UIKit + +/// Colors at https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1255%3A1104 +@objc public protocol Colors { + + /// - Focused/Active states + /// - CTAs + var accent: UIColor { get } + + /// - Error messages + /// - Content requiring user attention + /// - Notification, alerts + var alert: UIColor { get } + + /// - Text + /// - Icons + var primaryContent: UIColor { get } + + /// - Text + /// - Icons + var secondaryContent: UIColor { get } + + /// - Text + /// - Icons + var tertiaryContent: UIColor { get } + + /// - Text + /// - Icons + var quarterlyContent: UIColor { get } + + /// Separating line + var separator: UIColor { get } + + // Cards, tiles + var tile: UIColor { get } + + /// Top navigation background on iOS + var navigation: UIColor { get } + + /// Background UI color + var background: UIColor { get } + + /// - Names in chat timeline + /// - Avatars default states that include first name letter + var namesAndAvatars: [UIColor] { get } + +} diff --git a/DesignKit/Source/ThemeV2.swift b/DesignKit/Source/ThemeV2.swift new file mode 100644 index 000000000..48f310992 --- /dev/null +++ b/DesignKit/Source/ThemeV2.swift @@ -0,0 +1,28 @@ +// +// Copyright 2021 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import UIKit + +/// Theme v2. May be named again as `Theme` when the migration completed. +@objc public protocol ThemeV2 { + + /// Colors object + var colors: Colors { get } + + /// may contain more design components in future, like icons, audio files etc. + +} diff --git a/DesignKit/Variants/Dark/DarkColors.swift b/DesignKit/Variants/Dark/DarkColors.swift new file mode 100644 index 000000000..a1cf2ac54 --- /dev/null +++ b/DesignKit/Variants/Dark/DarkColors.swift @@ -0,0 +1,56 @@ +// +// Copyright 2021 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import UIKit + +/// Dark theme colors. Will be a struct when things are more Swifty. +public class DarkColors: Colors { + + public let accent: UIColor = UIColor(rgb: 0x0DBD8B) + + public let alert: UIColor = UIColor(rgb: 0xFF4B55) + + public let primaryContent: UIColor = UIColor(rgb: 0xFFFFFF) + + public let secondaryContent: UIColor = UIColor(rgb: 0xA9B2BC) + + public let tertiaryContent: UIColor = UIColor(rgb: 0x8E99A4) + + public let quarterlyContent: UIColor = UIColor(rgb: 0x6F7882) + + public let separator: UIColor = UIColor(rgb: 0x21262C) + + public let tile: UIColor = UIColor(rgb: 0x394049) + + public let navigation: UIColor = UIColor(rgb: 0x21262C) + + public let background: UIColor = UIColor(rgb: 0x15191E) + + public let namesAndAvatars: [UIColor] = [ + UIColor(rgb: 0x368BD6), + UIColor(rgb: 0xAC3BA8), + UIColor(rgb: 0x03B381), + UIColor(rgb: 0xE64F7A), + UIColor(rgb: 0xFF812D), + UIColor(rgb: 0x2DC2C5), + UIColor(rgb: 0x5C56F5), + UIColor(rgb: 0x74D12C) + ] + + public init() {} + +} diff --git a/DesignKit/Variants/Light/LightColors.swift b/DesignKit/Variants/Light/LightColors.swift new file mode 100644 index 000000000..d8b5e108b --- /dev/null +++ b/DesignKit/Variants/Light/LightColors.swift @@ -0,0 +1,56 @@ +// +// Copyright 2021 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import UIKit + +/// Light theme colors. Will be a struct when things are more Swifty. +public class LightColors: Colors { + + public let accent: UIColor = UIColor(rgb: 0x0DBD8B) + + public let alert: UIColor = UIColor(rgb: 0xFF4B55) + + public let primaryContent: UIColor = UIColor(rgb: 0x17191C) + + public let secondaryContent: UIColor = UIColor(rgb: 0x737D8C) + + public let tertiaryContent: UIColor = UIColor(rgb: 0x8D97A5) + + public let quarterlyContent: UIColor = UIColor(rgb: 0xC1C6CD) + + public let separator: UIColor = UIColor(rgb: 0xE3E8F0) + + public let tile: UIColor = UIColor(rgb: 0xF3F8FD) + + public let navigation: UIColor = UIColor(rgb: 0xF4F6FA) + + public let background: UIColor = UIColor(rgb: 0xFFFFFF) + + public let namesAndAvatars: [UIColor] = [ + UIColor(rgb: 0x368BD6), + UIColor(rgb: 0xAC3BA8), + UIColor(rgb: 0x03B381), + UIColor(rgb: 0xE64F7A), + UIColor(rgb: 0xFF812D), + UIColor(rgb: 0x2DC2C5), + UIColor(rgb: 0x5C56F5), + UIColor(rgb: 0x74D12C) + ] + + public init() {} + +} diff --git a/DesignKit/target.yml b/DesignKit/target.yml new file mode 100644 index 000000000..e10f76f12 --- /dev/null +++ b/DesignKit/target.yml @@ -0,0 +1,33 @@ +name: DesignKit + +schemes: + DesignKit: + analyze: + config: Debug + archive: + config: Release + build: + targets: + DesignKit: + - running + - profiling + - analyzing + - archiving + profile: + config: Release + run: + config: Debug + disableMainThreadChecker: true + +targets: + DesignKit: + type: framework + platform: iOS + + configFiles: + Debug: Debug.xcconfig + Release: Release.xcconfig + + sources: + - path: . + - path: ../Riot/Categories/UIColor.swift diff --git a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown.png b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown.png index 4e639fb9b..992d1edaf 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown.png and b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@2x.png b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@2x.png index aabb2b121..c21846293 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@2x.png and b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@3x.png b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@3x.png index 81685ed6b..936e57707 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@3x.png and b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown.imageset/scrolldown@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/scrolldown_dark.png b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/scrolldown_dark.png index 740a562f4..5d3228686 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/scrolldown_dark.png and b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/scrolldown_dark.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/scrolldown_dark@2x.png b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/scrolldown_dark@2x.png index 1dfe3ab3d..14a8900e0 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/scrolldown_dark@2x.png and b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/scrolldown_dark@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/scrolldown_dark@3x.png b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/scrolldown_dark@3x.png index 14ae3aa1b..729c73bb2 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/scrolldown_dark@3x.png and b/Riot/Assets/Images.xcassets/Room/Activities/scrolldown_dark.imageset/scrolldown_dark@3x.png differ diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index a102f2db7..277d820ce 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -828,8 +828,8 @@ Tap the + to start adding people."; "event_formatter_message_edited_mention" = "(edited)"; "event_formatter_call_voice" = "Voice call"; "event_formatter_call_video" = "Video call"; -"event_formatter_call_connecting" = "Connecting..."; -"event_formatter_call_ringing" = "Ringing..."; +"event_formatter_call_connecting" = "Connecting…"; +"event_formatter_call_ringing" = "Ringing…"; "event_formatter_call_has_ended" = "Ended %@"; "event_formatter_call_you_currently_in" = "Active call"; "event_formatter_call_you_declined" = "You declined this call"; diff --git a/Riot/Categories/MXSession+Riot.h b/Riot/Categories/MXSession+Riot.h index f756a2ede..d5fe928de 100644 --- a/Riot/Categories/MXSession+Riot.h +++ b/Riot/Categories/MXSession+Riot.h @@ -18,6 +18,8 @@ #import +@class HomeserverConfiguration; + @interface MXSession (Riot) /** @@ -26,15 +28,9 @@ - (NSUInteger)vc_missedDiscussionsCount; /** - Check if E2E by default is welcomed on the user's HS. - The default value is YES. - - HS admins can disable it in /.well-known/matrix/client by returning: - "im.vector.riot.e2ee": { - "default": false - } - */ -- (BOOL)vc_isE2EByDefaultEnabledByHSAdmin; +Return the homeserver configuration based on HS Well-Known or BuildSettings properties according to existing values. +*/ +- (HomeserverConfiguration*)vc_homeserverConfiguration; /** Riot version of [MXSession canEnableE2EByDefaultInNewRoomWithUsers:] diff --git a/Riot/Categories/MXSession+Riot.m b/Riot/Categories/MXSession+Riot.m index fd0a53c9d..1cc498024 100644 --- a/Riot/Categories/MXSession+Riot.m +++ b/Riot/Categories/MXSession+Riot.m @@ -49,25 +49,17 @@ return missedDiscussionsCount; } -- (BOOL)vc_isE2EByDefaultEnabledByHSAdmin +- (HomeserverConfiguration*)vc_homeserverConfiguration { - BOOL isE2EByDefaultEnabledByHSAdmin = YES; - - MXWellKnown *wellKnown = self.homeserverWellknown; - - if (wellKnown.JSONDictionary[@"im.vector.riot.e2ee"][@"default"]) - { - MXJSONModelSetBoolean(isE2EByDefaultEnabledByHSAdmin, wellKnown.JSONDictionary[@"im.vector.riot.e2ee"][@"default"]); - } - - return isE2EByDefaultEnabledByHSAdmin; + HomeserverConfigurationBuilder *configurationBuilder = [HomeserverConfigurationBuilder new]; + return [configurationBuilder buildFrom:self.homeserverWellknown]; } - (MXHTTPOperation*)vc_canEnableE2EByDefaultInNewRoomWithUsers:(NSArray*)userIds success:(void (^)(BOOL canEnableE2E))success failure:(void (^)(NSError *error))failure; { - if (self.vc_isE2EByDefaultEnabledByHSAdmin) + if ([self vc_homeserverConfiguration].isE2EEByDefaultEnabled) { return [self canEnableE2EByDefaultInNewRoomWithUsers:userIds success:success failure:failure]; } diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 2f16146b2..11d0dc52d 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1250,7 +1250,7 @@ internal enum VectorL10n { internal static var eventFormatterCallBack: String { return VectorL10n.tr("Vector", "event_formatter_call_back") } - /// Connecting... + /// Connecting… internal static var eventFormatterCallConnecting: String { return VectorL10n.tr("Vector", "event_formatter_call_connecting") } @@ -1274,7 +1274,7 @@ internal enum VectorL10n { internal static var eventFormatterCallRetry: String { return VectorL10n.tr("Vector", "event_formatter_call_retry") } - /// Ringing... + /// Ringing… internal static var eventFormatterCallRinging: String { return VectorL10n.tr("Vector", "event_formatter_call_ringing") } diff --git a/Riot/Managers/Call/CallPresenter.swift b/Riot/Managers/Call/CallPresenter.swift index 52f2fec4c..57a60b0cb 100644 --- a/Riot/Managers/Call/CallPresenter.swift +++ b/Riot/Managers/Call/CallPresenter.swift @@ -439,10 +439,6 @@ class CallPresenter: NSObject { return } - defer { - isStarted = true - } - NotificationCenter.default.addObserver(self, selector: #selector(newCall(_:)), name: NSNotification.Name(rawValue: kMXCallManagerNewCall), @@ -460,6 +456,8 @@ class CallPresenter: NSObject { name: .RoomGroupCallTileTapped, object: nil) + isStarted = true + #if canImport(JitsiMeetSDK) JMCallKitProxy.addListener(self) @@ -486,10 +484,6 @@ class CallPresenter: NSObject { return } - defer { - isStarted = false - } - NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: kMXCallManagerNewCall), object: nil) @@ -503,6 +497,8 @@ class CallPresenter: NSObject { name: .RoomGroupCallTileTapped, object: nil) + isStarted = false + #if canImport(JitsiMeetSDK) JMCallKitProxy.removeListener(self) diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index 5522b605e..954741ccb 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -60,6 +60,8 @@ final class RiotSettings: NSObject { static let roomSettingsScreenShowFlairSettings = "roomSettingsScreenShowFlairSettings" static let roomSettingsScreenShowAdvancedSettings = "roomSettingsScreenShowAdvancedSettings" static let roomSettingsScreenAdvancedShowEncryptToVerifiedOption = "roomSettingsScreenAdvancedShowEncryptToVerifiedOption" + static let settingsScreenShowNotificationDecodedContentOption = "settingsScreenShowNotificationDecodedContentOption" + static let settingsScreenShowNsfwRoomsOption = "settingsScreenShowNsfwRoomsOption" static let roomsAllowToJoinPublicRooms = "roomsAllowToJoinPublicRooms" static let homeScreenShowFavouritesTab = "homeScreenShowFavouritesTab" static let homeScreenShowPeopleTab = "homeScreenShowPeopleTab" @@ -71,7 +73,11 @@ final class RiotSettings: NSObject { static let roomScreenAllowMediaLibraryAction = "roomScreenAllowMediaLibraryAction" static let roomScreenAllowStickerAction = "roomScreenAllowStickerAction" static let roomScreenAllowFilesAction = "roomScreenAllowFilesAction" + static let roomContextualMenuShowMoreOptionForMessages = "roomContextualMenuShowMoreOptionForMessages" + static let roomContextualMenuShowMoreOptionForStates = "roomContextualMenuShowMoreOptionForStates" + static let roomContextualMenuShowReportContentOption = "roomContextualMenuShowReportContentOption" static let roomInfoScreenShowIntegrations = "roomInfoScreenShowIntegrations" + static let roomMemberScreenShowIgnore = "roomMemberScreenShowIgnore" static let unifiedSearchScreenShowPublicDirectory = "unifiedSearchScreenShowPublicDirectory" } @@ -323,6 +329,39 @@ final class RiotSettings: NSObject { defaults.set(newValue, forKey: UserDefaultsKeys.roomScreenAllowFilesAction) } } + + // MARK: - Room Contextual Menu + + var roomContextualMenuShowMoreOptionForMessages: Bool { + get { + guard defaults.object(forKey: UserDefaultsKeys.roomContextualMenuShowMoreOptionForMessages) != nil else { + return BuildSettings.roomContextualMenuShowMoreOptionForMessages + } + return defaults.bool(forKey: UserDefaultsKeys.roomContextualMenuShowMoreOptionForMessages) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.roomContextualMenuShowMoreOptionForMessages) + } + } + var roomContextualMenuShowMoreOptionForStates: Bool { + get { + guard defaults.object(forKey: UserDefaultsKeys.roomContextualMenuShowMoreOptionForStates) != nil else { + return BuildSettings.roomContextualMenuShowMoreOptionForStates + } + return defaults.bool(forKey: UserDefaultsKeys.roomContextualMenuShowMoreOptionForStates) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.roomContextualMenuShowMoreOptionForStates) + } + } + var roomContextualMenuShowReportContentOption: Bool { + get { + guard defaults.object(forKey: UserDefaultsKeys.roomContextualMenuShowReportContentOption) != nil else { + return BuildSettings.roomContextualMenuShowReportContentOption + } + return defaults.bool(forKey: UserDefaultsKeys.roomContextualMenuShowReportContentOption) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.roomContextualMenuShowReportContentOption) + } + } // MARK: - Room Info Screen @@ -337,6 +376,19 @@ final class RiotSettings: NSObject { } } + // MARK: - Room Member Screen + + var roomMemberScreenShowIgnore: Bool { + get { + guard defaults.object(forKey: UserDefaultsKeys.roomMemberScreenShowIgnore) != nil else { + return BuildSettings.roomMemberScreenShowIgnore + } + return defaults.bool(forKey: UserDefaultsKeys.roomMemberScreenShowIgnore) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.roomMemberScreenShowIgnore) + } + } + // MARK: - Room Creation Screen var roomCreationScreenAllowEncryptionConfiguration: Bool { @@ -468,6 +520,26 @@ final class RiotSettings: NSObject { defaults.set(newValue, forKey: UserDefaultsKeys.settingsScreenShowEnableStunServerFallback) } } + var settingsScreenShowNotificationDecodedContentOption: Bool { + get { + guard defaults.object(forKey: UserDefaultsKeys.settingsScreenShowNotificationDecodedContentOption) != nil else { + return BuildSettings.settingsScreenShowNotificationDecodedContentOption + } + return defaults.bool(forKey: UserDefaultsKeys.settingsScreenShowNotificationDecodedContentOption) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.settingsScreenShowNotificationDecodedContentOption) + } + } + var settingsScreenShowNsfwRoomsOption: Bool { + get { + guard defaults.object(forKey: UserDefaultsKeys.settingsScreenShowNsfwRoomsOption) != nil else { + return BuildSettings.settingsScreenShowNsfwRoomsOption + } + return defaults.bool(forKey: UserDefaultsKeys.settingsScreenShowNsfwRoomsOption) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.settingsScreenShowNsfwRoomsOption) + } + } var settingsSecurityScreenShowSessions: Bool { get { guard defaults.object(forKey: UserDefaultsKeys.settingsSecurityScreenShowSessions) != nil else { diff --git a/Riot/Managers/Theme/Theme.swift b/Riot/Managers/Theme/Theme.swift index a70e9d993..0511d5085 100644 --- a/Riot/Managers/Theme/Theme.swift +++ b/Riot/Managers/Theme/Theme.swift @@ -15,10 +15,11 @@ */ import UIKit +import DesignKit /// Provide color constant values defined by the designer /// https://app.zeplin.io/project/5c122fa790c5b4241ffa6be7/screen/5c619592daff2f1241d82e75 -@objc protocol Theme { +@objc protocol Theme: ThemeV2 { var identifier: String { get } diff --git a/Riot/Managers/Theme/Themes/DarkTheme.swift b/Riot/Managers/Theme/Themes/DarkTheme.swift index 77f0436d8..cb097911e 100644 --- a/Riot/Managers/Theme/Themes/DarkTheme.swift +++ b/Riot/Managers/Theme/Themes/DarkTheme.swift @@ -16,6 +16,7 @@ import Foundation import UIKit +import DesignKit /// Color constants for the dark theme @objcMembers @@ -141,4 +142,10 @@ class DarkTheme: NSObject, Theme { button.tintColor = self.tintColor button.setTitleColor(self.tintColor, for: .normal) } + + /// MARK: - Theme v2 + + lazy var colors: Colors = { + return DarkColors() + }() } diff --git a/Riot/Managers/Theme/Themes/DefaultTheme.swift b/Riot/Managers/Theme/Themes/DefaultTheme.swift index 58136b119..61cf6ebb9 100644 --- a/Riot/Managers/Theme/Themes/DefaultTheme.swift +++ b/Riot/Managers/Theme/Themes/DefaultTheme.swift @@ -16,6 +16,7 @@ import Foundation import UIKit +import DesignKit /// Color constants for the default theme @objcMembers @@ -148,4 +149,10 @@ class DefaultTheme: NSObject, Theme { button.tintColor = self.tintColor button.setTitleColor(self.tintColor, for: .normal) } + + /// MARK: - Theme v2 + + lazy var colors: Colors = { + return LightColors() + }() } diff --git a/Riot/Managers/Widgets/WidgetManager.m b/Riot/Managers/Widgets/WidgetManager.m index 941ac7906..ac212da1a 100644 --- a/Riot/Managers/Widgets/WidgetManager.m +++ b/Riot/Managers/Widgets/WidgetManager.m @@ -19,6 +19,7 @@ #import "Riot-Swift.h" #import "JitsiWidgetData.h" +#import "MXSession+Riot.h" #import @@ -278,7 +279,7 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; // Riot-Web still uses V1 type NSString *widgetId = [NSString stringWithFormat:@"%@_%@_%@", kWidgetTypeJitsiV1, room.mxSession.myUser.userId, @((uint64_t)([[NSDate date] timeIntervalSince1970] * 1000))]; - NSURL *preferredJitsiServerUrl = BuildSettings.jitsiServerUrl; + NSURL *preferredJitsiServerUrl = [room.mxSession vc_homeserverConfiguration].jitsi.serverURL; JitsiService *jitsiService = JitsiService.shared; diff --git a/Riot/Model/HomeserverConfiguration/HomeserverConfiguration.swift b/Riot/Model/HomeserverConfiguration/HomeserverConfiguration.swift new file mode 100644 index 000000000..f09d8608c --- /dev/null +++ b/Riot/Model/HomeserverConfiguration/HomeserverConfiguration.swift @@ -0,0 +1,34 @@ +// +// Copyright 2020 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// Represents the homeserver configuration (usually based on HS Well-Known or hardoced values in the project) +@objcMembers +final class HomeserverConfiguration: NSObject { + + // Note: Use an object per configuration subject when there is multiple properties related + let jitsi: HomeserverJitsiConfiguration + let isE2EEByDefaultEnabled: Bool + + init(jitsi: HomeserverJitsiConfiguration, + isE2EEByDefaultEnabled: Bool) { + self.jitsi = jitsi + self.isE2EEByDefaultEnabled = isE2EEByDefaultEnabled + + super.init() + } +} diff --git a/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift b/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift new file mode 100644 index 000000000..c77ff6bab --- /dev/null +++ b/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift @@ -0,0 +1,125 @@ +// +// Copyright 2020 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// `HomeserverConfigurationBuilder` build `HomeserverConfiguration` objects according to injected inputs +@objcMembers +final class HomeserverConfigurationBuilder: NSObject { + + // MARK: - Properties + + private let vectorWellKnownParser = VectorWellKnownParser() + + // MARK: - Public + + /// Create an `HomeserverConfiguration` from an HS Well-Known when possible otherwise it takes hardcoded values from BuildSettings by default. + func build(from wellKnown: MXWellKnown?) -> HomeserverConfiguration { + + let isE2EEByDefaultEnabled: Bool + let jitsiPreferredDomain: String + + var vectorWellKnownEncryptionConfiguration: VectorWellKnownEncryptionConfiguration? + var vectorWellKnownJitsiConfiguration: VectorWellKnownJitsiConfiguration? + + if let wellKnown = wellKnown, let vectorWellKnown = self.vectorWellKnownParser.parse(jsonDictionary: wellKnown.jsonDictionary()) { + vectorWellKnownEncryptionConfiguration = self.getEncryptionConfiguration(from: vectorWellKnown) + vectorWellKnownJitsiConfiguration = self.getJitsiConfiguration(from: vectorWellKnown) + } + + // Encryption configuration + if let vectorWellKnownEncryptionConfig = vectorWellKnownEncryptionConfiguration { + isE2EEByDefaultEnabled = vectorWellKnownEncryptionConfig.isE2EEByDefaultEnabled + } else { + // Enable E2EE by default when there is no value + isE2EEByDefaultEnabled = true + } + + // Jitsi configuration + let jitsiServerURL: URL + let hardcodedJitsiServerURL: URL = BuildSettings.jitsiServerUrl + + if let vectorWellKnownJitsiConfig = vectorWellKnownJitsiConfiguration { + jitsiPreferredDomain = vectorWellKnownJitsiConfig.preferredDomain + jitsiServerURL = self.jitsiServerURL(from: jitsiPreferredDomain) ?? hardcodedJitsiServerURL + } else { + guard let hardcodedJitsiDomain = hardcodedJitsiServerURL.host else { + fatalError("[HomeserverConfigurationBuilder] Fail to get Jitsi domain from hardcoded Jitsi URL") + } + jitsiPreferredDomain = hardcodedJitsiDomain + jitsiServerURL = hardcodedJitsiServerURL + } + + // Create HomeserverConfiguration + + let jitsiConfiguration = HomeserverJitsiConfiguration(serverDomain: jitsiPreferredDomain, + serverURL: jitsiServerURL) + + return HomeserverConfiguration(jitsi: jitsiConfiguration, isE2EEByDefaultEnabled: isE2EEByDefaultEnabled) + } + + // MARK: - Private + + private func getJitsiConfiguration(from vectorWellKnown: VectorWellKnown) -> VectorWellKnownJitsiConfiguration? { + + let jitsiConfiguration: VectorWellKnownJitsiConfiguration? + + if let lastJitsiConfiguration = vectorWellKnown.jitsi { + jitsiConfiguration = lastJitsiConfiguration + } else if let deprecatedJitsiConfiguration = vectorWellKnown.deprecatedJitsi { + NSLog("[HomeserverConfigurationBuilder] getJitsiConfiguration - Use deprecated configuration") + jitsiConfiguration = deprecatedJitsiConfiguration + } else { + NSLog("[HomeserverConfigurationBuilder] getJitsiConfiguration - No configuration found") + jitsiConfiguration = nil + } + + return jitsiConfiguration + } + + private func getEncryptionConfiguration(from vectorWellKnown: VectorWellKnown) -> VectorWellKnownEncryptionConfiguration? { + + let encryptionConfiguration: VectorWellKnownEncryptionConfiguration? + + if let lastEncryptionConfiguration = vectorWellKnown.encryption { + encryptionConfiguration = lastEncryptionConfiguration + } else if let deprecatedEncryptionConfiguration = vectorWellKnown.deprecatedEncryption { + NSLog("[HomeserverConfigurationBuilder] getEncryptionConfiguration - Use deprecated configuration") + encryptionConfiguration = deprecatedEncryptionConfiguration + } else { + NSLog("[HomeserverConfigurationBuilder] getEncryptionConfiguration - No configuration found") + encryptionConfiguration = nil + } + + return encryptionConfiguration + } + + private func jitsiServerURL(from jitsiServerDomain: String) -> URL? { + let jitsiStringURL: String + if jitsiServerDomain.starts(with: "http") { + jitsiStringURL = jitsiServerDomain + } else { + jitsiStringURL = "https://\(jitsiServerDomain)" + } + + guard let jitsiServerURL = URL(string: jitsiStringURL) else { + NSLog("[HomeserverConfigurationBuilder] Jitsi server URL is not valid") + return nil + } + + return jitsiServerURL + } +} diff --git a/Riot/Model/HomeserverConfiguration/HomeserverJitsiConfiguration.swift b/Riot/Model/HomeserverConfiguration/HomeserverJitsiConfiguration.swift new file mode 100644 index 000000000..e5e93bed0 --- /dev/null +++ b/Riot/Model/HomeserverConfiguration/HomeserverJitsiConfiguration.swift @@ -0,0 +1,31 @@ +// +// Copyright 2020 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// `HomeserverJitsiConfiguration` gives Jitsi widget configuration used by homeserver +@objcMembers +final class HomeserverJitsiConfiguration: NSObject { + let serverDomain: String + let serverURL: URL + + init(serverDomain: String, serverURL: URL) { + self.serverDomain = serverDomain + self.serverURL = serverURL + + super.init() + } +} diff --git a/Riot/Model/WellKnown/VectorWellKnown.swift b/Riot/Model/WellKnown/VectorWellKnown.swift new file mode 100644 index 000000000..2a44c0c19 --- /dev/null +++ b/Riot/Model/WellKnown/VectorWellKnown.swift @@ -0,0 +1,60 @@ +// +// Copyright 2020 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +// MARK: - Well Known + +/// `VectorWellKnown` represents additional Well Known configuration specific to Element client +struct VectorWellKnown { + let encryption: VectorWellKnownEncryptionConfiguration? + let jitsi: VectorWellKnownJitsiConfiguration? + + // Deprecated properties + let deprecatedEncryption: VectorWellKnownEncryptionConfiguration? + let deprecatedJitsi: VectorWellKnownJitsiConfiguration? +} + +// MARK: Decodable +extension VectorWellKnown: Decodable { + /// JSON keys associated to VectorWellKnown properties + enum CodingKeys: String, CodingKey { + case encryption = "io.element.e2ee" + case jitsi = "io.element.jitsi" + // Deprecated keys + case deprecatedEncryption = "im.vector.riot.e2ee" + case deprecatedJitsi = "im.vector.riot.jitsi" + } +} + +// MARK: - Encryption + +struct VectorWellKnownEncryptionConfiguration: Decodable { + + /// Indicate if E2EE is enabled by default + let isE2EEByDefaultEnabled: Bool + + enum CodingKeys: String, CodingKey { + case isE2EEByDefaultEnabled = "default" + } +} + +// MARK: - Jitsi +struct VectorWellKnownJitsiConfiguration: Decodable { + + /// Default Jitsi server + let preferredDomain: String +} diff --git a/Riot/Model/WellKnown/VectorWellKnownParser.swift b/Riot/Model/WellKnown/VectorWellKnownParser.swift new file mode 100644 index 000000000..3fc7325c6 --- /dev/null +++ b/Riot/Model/WellKnown/VectorWellKnownParser.swift @@ -0,0 +1,34 @@ +// +// Copyright 2020 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +final class VectorWellKnownParser { + + func parse(jsonDictionary: [AnyHashable: Any]) -> VectorWellKnown? { + let serializationService = SerializationService() + let vectorWellKnown: VectorWellKnown? + + do { + vectorWellKnown = try serializationService.deserialize(jsonDictionary) + } catch { + vectorWellKnown = nil + NSLog("[VectorWellKnownParser] Fail to parse application Well Known keys with error: \(error)") + } + + return vectorWellKnown + } +} diff --git a/Riot/Modules/Authentication/AuthenticationViewController.m b/Riot/Modules/Authentication/AuthenticationViewController.m index 0a738a370..48e73eda0 100644 --- a/Riot/Modules/Authentication/AuthenticationViewController.m +++ b/Riot/Modules/Authentication/AuthenticationViewController.m @@ -1430,7 +1430,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; // TODO: This is still not sure we want to disable the automatic cross-signing bootstrap // if the admin disabled e2e by default. // Do like riot-web for the moment - if (session.vc_isE2EByDefaultEnabledByHSAdmin) + if ([session vc_homeserverConfiguration].isE2EEByDefaultEnabled) { // Bootstrap cross-signing on user's account // We do it for both registration and new login as long as cross-signing does not exist yet diff --git a/Riot/Modules/Call/Dialpad/Views/DialpadButton.swift b/Riot/Modules/Call/Dialpad/Views/DialpadButton.swift index 0ea0a2661..f39394e19 100644 --- a/Riot/Modules/Call/Dialpad/Views/DialpadButton.swift +++ b/Riot/Modules/Call/Dialpad/Views/DialpadButton.swift @@ -20,10 +20,10 @@ import UIKit class DialpadButton: UIButton { struct ViewData { - var title: String - var tone: SystemSoundID - var subtitle: String? - var showsSubtitleSpace: Bool + let title: String + let tone: SystemSoundID + let subtitle: String? + let showsSubtitleSpace: Bool init(title: String, tone: SystemSoundID, subtitle: String? = nil, showsSubtitleSpace: Bool = false) { self.title = title diff --git a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift index a9a48bdde..4967b5161 100644 --- a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift +++ b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift @@ -39,7 +39,7 @@ final class EnterNewRoomDetailsViewModel: EnterNewRoomDetailsViewModelType { init(session: MXSession) { self.session = session - roomCreationParameters.isEncrypted = session.vc_isE2EByDefaultEnabledByHSAdmin() && RiotSettings.shared.roomCreationScreenRoomIsEncrypted + roomCreationParameters.isEncrypted = session.vc_homeserverConfiguration().isE2EEByDefaultEnabled && RiotSettings.shared.roomCreationScreenRoomIsEncrypted roomCreationParameters.isPublic = RiotSettings.shared.roomCreationScreenRoomIsPublic } diff --git a/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m b/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m index 368d5993d..82a621795 100644 --- a/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m +++ b/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m @@ -628,7 +628,7 @@ } // Check whether the option Ignore may be presented - if (self.mxRoomMember.membership == MXMembershipJoin) + if (RiotSettings.shared.roomMemberScreenShowIgnore && self.mxRoomMember.membership == MXMembershipJoin) { // is he already ignored ? if (![self.mainSession isUserIgnored:self.mxRoomMember.userId]) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index b063db1f8..0c554a01f 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -3226,7 +3226,7 @@ const NSUInteger kJumpToUnreadCloseButtonTintColorForDarkMode = 0x6F7882; } } - if (![selectedEvent.sender isEqualToString:self.mainSession.myUser.userId]) + if (![selectedEvent.sender isEqualToString:self.mainSession.myUser.userId] && RiotSettings.shared.roomContextualMenuShowReportContentOption) { [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_report", @"Vector", nil) style:UIAlertActionStyleDefault @@ -5514,12 +5514,25 @@ const NSUInteger kJumpToUnreadCloseButtonTintColorForDarkMode = 0x6F7882; ]; } - return @[ - [self copyMenuItemWithEvent:event andCell:cell], - [self replyMenuItemWithEvent:event], - [self editMenuItemWithEvent:event], - [self moreMenuItemWithEvent:event andCell:cell] - ]; + BOOL showMoreOption = (event.isState && RiotSettings.shared.roomContextualMenuShowMoreOptionForStates) || (!event.isState && RiotSettings.shared.roomContextualMenuShowMoreOptionForMessages); + + if (showMoreOption) + { + return @[ + [self copyMenuItemWithEvent:event andCell:cell], + [self replyMenuItemWithEvent:event], + [self editMenuItemWithEvent:event], + [self moreMenuItemWithEvent:event andCell:cell] + ]; + } + else + { + return @[ + [self copyMenuItemWithEvent:event andCell:cell], + [self replyMenuItemWithEvent:event], + [self editMenuItemWithEvent:event] + ]; + } } - (void)showContextualMenuForEvent:(MXEvent*)event fromSingleTapGesture:(BOOL)usedSingleTapGesture cell:(id)cell animated:(BOOL)animated diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index 2c238e07e..0ae7fb7c2 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -350,7 +350,10 @@ TableViewSectionsDelegate> Section *sectionNotificationSettings = [Section sectionWithTag:SECTION_TAG_NOTIFICATIONS]; [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_ENABLE_PUSH_INDEX]; - [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_SHOW_DECODED_CONTENT]; + if (RiotSettings.shared.settingsScreenShowNotificationDecodedContentOption) + { + [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_SHOW_DECODED_CONTENT]; + } [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_GLOBAL_SETTINGS_INDEX]; [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_PIN_MISSED_NOTIFICATIONS_INDEX]; [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_PIN_UNREAD_INDEX]; @@ -458,7 +461,10 @@ TableViewSectionsDelegate> [sectionOther addRowWithTag:OTHER_PRIVACY_INDEX]; } [sectionOther addRowWithTag:OTHER_THIRD_PARTY_INDEX]; - [sectionOther addRowWithTag:OTHER_SHOW_NSFW_ROOMS_INDEX]; + if (RiotSettings.shared.settingsScreenShowNsfwRoomsOption) + { + [sectionOther addRowWithTag:OTHER_SHOW_NSFW_ROOMS_INDEX]; + } if (BuildSettings.settingsScreenAllowChangingCrashUsageDataSettings) { diff --git a/Riot/target.yml b/Riot/target.yml index ccd717311..7a0d46b75 100644 --- a/Riot/target.yml +++ b/Riot/target.yml @@ -34,6 +34,7 @@ targets: - target: RiotShareExtension - target: SiriIntents - target: RiotNSE + - target: DesignKit configFiles: Debug: Debug.xcconfig diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 032d02ee7..ec2230cb9 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -374,21 +374,19 @@ class NotificationService: UNNotificationServiceExtension { notificationBody = NSString.localizedUserNotificationString(forKey: "STICKER_FROM_USER", arguments: [eventSenderName as Any]) case .custom: - if event.type == kWidgetMatrixEventTypeString || event.type == kWidgetModularEventTypeString { - if let content = event.content, let type = content["type"] as? String { - if type == kWidgetTypeJitsiV1 || type == kWidgetTypeJitsiV2 { - notificationBody = NSString.localizedUserNotificationString(forKey: "GROUP_CALL_STARTED", arguments: nil) - notificationTitle = roomDisplayName - - // call notifications should stand out from normal messages, so we don't stack them - threadIdentifier = nil - // only send VoIP pushes if ringing is enabled for group calls - if RiotSettings.shared.enableRingingForGroupCalls { - self.sendVoipPush(forEvent: event) - } else { - additionalUserInfo = [Constants.userInfoKeyPresentNotificationOnForeground: true] - } - } + if (event.type == kWidgetMatrixEventTypeString || event.type == kWidgetModularEventTypeString), + let type = event.content?["type"] as? String, + (type == kWidgetTypeJitsiV1 || type == kWidgetTypeJitsiV2) { + notificationBody = NSString.localizedUserNotificationString(forKey: "GROUP_CALL_STARTED", arguments: nil) + notificationTitle = roomDisplayName + + // call notifications should stand out from normal messages, so we don't stack them + threadIdentifier = nil + // only send VoIP pushes if ringing is enabled for group calls + if RiotSettings.shared.enableRingingForGroupCalls { + self.sendVoipPush(forEvent: event) + } else { + additionalUserInfo = [Constants.userInfoKeyPresentNotificationOnForeground: true] } } default: diff --git a/RiotTests/HomeserverConfigurationTests.swift b/RiotTests/HomeserverConfigurationTests.swift new file mode 100644 index 000000000..d13ef9211 --- /dev/null +++ b/RiotTests/HomeserverConfigurationTests.swift @@ -0,0 +1,71 @@ +// +// Copyright 2020 New Vector 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 XCTest + +@testable import Riot + +class HomeserverConfigurationTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + // MARK: - Tests + + func testHomeserverConfigurationBuilder() { + + let expectedJitsiServer = "your.jitsi.example.org" + let expectedJitsiServerStringURL = "https://\(expectedJitsiServer)" + let expectedDeprecatedJitsiServer = "your.deprecated.jitsi.example.org" + let expectedE2EEEByDefaultEnabled = true + let expectedDeprecatedE2EEEByDefaultEnabled = false + + let wellKnownDictionary: [String: Any] = [ + "m.homeserver": [ + "base_url": "https://your.homeserver.org" + ], + "m.identity_server": [ + "base_url": "https://your.identity-server.org" + ], + "im.vector.riot.e2ee" : [ + "default" : expectedDeprecatedE2EEEByDefaultEnabled + ], + "im.vector.riot.jitsi" : [ + "preferredDomain" : expectedDeprecatedJitsiServer + ], + "io.element.e2ee" : [ + "default" : expectedE2EEEByDefaultEnabled + ], + "io.element.jitsi" : [ + "preferredDomain" : expectedJitsiServer + ] + ] + + let wellKnown = MXWellKnown(fromJSON: wellKnownDictionary) + + let homeserverConfigurationBuilder = HomeserverConfigurationBuilder() + let homeserverConfiguration = homeserverConfigurationBuilder.build(from: wellKnown) + + XCTAssertEqual(homeserverConfiguration.jitsi.serverDomain, expectedJitsiServer) + XCTAssertEqual(homeserverConfiguration.jitsi.serverURL.absoluteString, expectedJitsiServerStringURL) + XCTAssertEqual(homeserverConfiguration.isE2EEByDefaultEnabled, expectedE2EEEByDefaultEnabled) + } +} diff --git a/RiotTests/VectorWellKnownTests.swift b/RiotTests/VectorWellKnownTests.swift new file mode 100644 index 000000000..d27ebf020 --- /dev/null +++ b/RiotTests/VectorWellKnownTests.swift @@ -0,0 +1,104 @@ +// +// Copyright 2020 New Vector 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 XCTest + +@testable import Riot + +class VectorWellKnownTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + // MARK: - Tests + + func testVectorWellKnownParsing() { + + let expectedJitsiServer = "your.jitsi.example.org" + let expectedE2EEEByDefaultEnabled = false + + let wellKnownDictionary: [String: Any] = [ + "im.vector.riot.e2ee" : [ + "default" : expectedE2EEEByDefaultEnabled + ], + "im.vector.riot.jitsi" : [ + "preferredDomain" : expectedJitsiServer + ], + "io.element.e2ee" : [ + "default" : expectedE2EEEByDefaultEnabled + ], + "io.element.jitsi" : [ + "preferredDomain" : expectedJitsiServer + ] + ] + + let serializationService = SerializationService() + + do { + let vectorWellKnown: VectorWellKnown = try serializationService.deserialize(wellKnownDictionary) + + let jistiConfiguration = vectorWellKnown.jitsi + let encryptionConfiguration = vectorWellKnown.encryption + + XCTAssertNotNil(jistiConfiguration) + XCTAssertNotNil(encryptionConfiguration) + + XCTAssertEqual(jistiConfiguration?.preferredDomain, expectedJitsiServer) + XCTAssertEqual(encryptionConfiguration?.isE2EEByDefaultEnabled, expectedE2EEEByDefaultEnabled) + + let deprecatedJistiConfiguration = vectorWellKnown.deprecatedJitsi + let deprecatedEncryptionConfiguration = vectorWellKnown.deprecatedEncryption + + XCTAssertNotNil(deprecatedJistiConfiguration) + XCTAssertNotNil(deprecatedEncryptionConfiguration) + + XCTAssertEqual(deprecatedJistiConfiguration?.preferredDomain, expectedJitsiServer) + XCTAssertEqual(deprecatedEncryptionConfiguration?.isE2EEByDefaultEnabled, expectedE2EEEByDefaultEnabled) + + } catch { + XCTFail("Fail with error: \(error)") + } + } + + func testVectorWellKnownParsingMissingKey() { + + let expectedE2EEEByDefaultEnabled = false + + let wellKnownDictionary: [String: Any] = [ + "io.element.e2ee" : [ + "default" : expectedE2EEEByDefaultEnabled + ] + ] + + let serializationService = SerializationService() + + do { + let vectorWellKnown: VectorWellKnown = try serializationService.deserialize(wellKnownDictionary) + + XCTAssertNil(vectorWellKnown.jitsi) + XCTAssertNotNil(vectorWellKnown.encryption) + + XCTAssertEqual(vectorWellKnown.encryption?.isE2EEByDefaultEnabled, expectedE2EEEByDefaultEnabled) + } catch { + XCTFail("Fail with error: \(error)") + } + } +} diff --git a/project.yml b/project.yml index 729329e43..b2384fd09 100644 --- a/project.yml +++ b/project.yml @@ -30,4 +30,5 @@ include: - path: RiotTests/target.yml - path: RiotShareExtension/target.yml - path: SiriIntents/target.yml - - path: RiotNSE/target.yml \ No newline at end of file + - path: RiotNSE/target.yml + - path: DesignKit/target.yml