Commit ff410f4a authored by Jose Blaya's avatar Jose Blaya

Merge branch 'feature/inapp-messages' into develop

parents 9f9facb8 de7bd6f1
......@@ -246,6 +246,8 @@
82C374F82514DE7D00E391EE /* EndpointManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82C374F42514DC6D00E391EE /* EndpointManagerTests.swift */; };
82C374F92514DE8200E391EE /* server.json in Resources */ = {isa = PBXBuildFile; fileRef = 82C374F62514DC7200E391EE /* server.json */; };
82C374FB2514DEC700E391EE /* EndpointManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82C374FA2514DEC700E391EE /* EndpointManager.swift */; };
82CAB808255A9ACB00BB08EF /* InAppMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAB807255A9ACB00BB08EF /* InAppMessage.swift */; };
82CAB809255A9ACB00BB08EF /* InAppMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAB807255A9ACB00BB08EF /* InAppMessage.swift */; };
82DDD5302539CFDC0049E79E /* DIPTokenKeychainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82DDD52F2539CFDC0049E79E /* DIPTokenKeychainTests.swift */; };
82E20B1124F652ED0065EFE3 /* AccountInfo+Kotlin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82E20B1024F652ED0065EFE3 /* AccountInfo+Kotlin.swift */; };
82E20B1224F652ED0065EFE3 /* AccountInfo+Kotlin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82E20B1024F652ED0065EFE3 /* AccountInfo+Kotlin.swift */; };
......@@ -568,6 +570,7 @@
82C374F42514DC6D00E391EE /* EndpointManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EndpointManagerTests.swift; sourceTree = "<group>"; };
82C374F62514DC7200E391EE /* server.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = server.json; sourceTree = "<group>"; };
82C374FA2514DEC700E391EE /* EndpointManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EndpointManager.swift; sourceTree = "<group>"; };
82CAB807255A9ACB00BB08EF /* InAppMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppMessage.swift; sourceTree = "<group>"; };
82DDD52F2539CFDC0049E79E /* DIPTokenKeychainTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DIPTokenKeychainTests.swift; sourceTree = "<group>"; };
82E20B1024F652ED0065EFE3 /* AccountInfo+Kotlin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountInfo+Kotlin.swift"; sourceTree = "<group>"; };
82E20B1524F6AA110065EFE3 /* RegionData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegionData.swift; sourceTree = "<group>"; };
......@@ -865,6 +868,7 @@
DD6768E222FAB19D00B9FDD0 /* AppStoreInformation.swift */,
82E20B1524F6AA110065EFE3 /* RegionData.swift */,
829EB63E2535C432003E74DD /* DedicatedIP.swift */,
82CAB807255A9ACB00BB08EF /* InAppMessage.swift */,
);
path = WebServices;
sourceTree = "<group>";
......@@ -1747,6 +1751,7 @@
DD8C3E652327EF6000BAD18E /* IKEv2EncryptionAlgorithm.swift in Sources */,
0EF14E4C1FEAE6350007485A /* Client+Providers.swift in Sources */,
0E7BC6E61F96B1000035C8B2 /* Client.swift in Sources */,
82CAB809255A9ACB00BB08EF /* InAppMessage.swift in Sources */,
0E392D7E1FE2E4C10002160D /* MemoryStore.swift in Sources */,
DD76292921ECDFF80092DF50 /* Usage.swift in Sources */,
0E2ADD381FE14F0000BB170C /* DefaultVPNProvider.swift in Sources */,
......@@ -1819,6 +1824,7 @@
0EC849C91F82329F002480CA /* WebServices.swift in Sources */,
0E9D62711FDE83BD009A90CF /* GlossServer.swift in Sources */,
0EE14D1B1FF16B39008D9AC2 /* InvalidatingFlowLayout.swift in Sources */,
82CAB808255A9ACB00BB08EF /* InAppMessage.swift in Sources */,
82183D7F25011D200033023F /* MagicLinkLoginViewController.swift in Sources */,
0EE1068C1F8250A1009514E9 /* Plan.swift in Sources */,
DD36CB7E21CCFFFB00FC815A /* CAGradientLayer+Image.swift in Sources */,
......
......@@ -144,7 +144,13 @@ public protocol AccountProvider: class {
*/
func featureFlags(_ callback: SuccessLibraryCallback?)
/**
Returns the available messages from the API.
- Parameter callback: Returns the message`InAppMessage` on success.
*/
func inAppMessages(_ callback: LibraryCallback<InAppMessage>?)
#if os(iOS)
/**
Lists the available plans with their corresponding product to purchase in order to get them.
......
......@@ -31,6 +31,11 @@ public enum AvailableTiles: Int, EnumsBuilder {
case quickSettings
case favoriteServers
case connectionTile
case messages
public static func fixedTiles() -> [AvailableTiles] {
return [.messages]
}
public static func allTiles() -> [AvailableTiles] {
return [.region, .quickConnect, .ip, .subscription, .usage, .quickSettings, .favoriteServers, .connectionTile]
......
......@@ -28,6 +28,9 @@ public protocol TileProvider: class {
/// the visible tiles in the dashboard.
var visibleTiles: [AvailableTiles] { get set }
/// the fixed tiles in the dashboard.
var fixedTiles: [AvailableTiles] { get }
/// the order of the tiles to appear in the dashboard.
var orderedTiles: [AvailableTiles] { get set }
......
//
// InAppMessage.swift
// PIALibrary
//
// Created by Jose Blaya on 10/11/2020.
// Copyright © 2020 Private Internet Access, Inc.
//
// This file is part of the Private Internet Access iOS Client.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
import PIAAccount
public enum InAppMessageType {
case action
case view
case link
}
public enum InAppMessageLevel {
case system
case api
}
public struct InAppMessage {
public let id: String
public let message: [String: String]
public let linkMessage: [String: String]
public let type: InAppMessageType
public let level: InAppMessageLevel
public let settingAction: [String: Bool]?
public let settingView: String?
public let settingLink: String?
public init(withMessage message: [String: String], id: String, link: [String: String], type: InAppMessageType, level: InAppMessageLevel, actions: [String:Bool]?, view: String?, uri: String?) {
self.id = id
self.message = message
self.linkMessage = link
self.type = type
self.level = level
self.settingAction = actions
self.settingView = view
self.settingLink = uri
}
}
extension InAppMessage {
init(withMessage messageInformation: MessageInformation, andLevel level: InAppMessageLevel) {
self.id = messageInformation.id
self.message = messageInformation.message
self.linkMessage = messageInformation.link.text
if !messageInformation.link.action.settings.isEmpty {
self.type = .action
var actions = [String: Bool]()
for setting in messageInformation.link.action.settings {
actions[setting.key] = setting.value.boolValue
}
self.settingAction = actions
self.settingLink = nil
self.settingView = nil
} else if !messageInformation.link.action.uri.isEmpty {
self.type = .link
self.settingLink = messageInformation.link.action.uri
self.settingAction = nil
self.settingView = nil
} else {
self.type = .view
self.settingView = messageInformation.link.action.view
self.settingLink = nil
self.settingAction = nil
}
self.level = level
}
}
......@@ -72,4 +72,7 @@ protocol WebServices: class {
func featureFlags(_ callback: LibraryCallback<[String]>?)
// MARK: Messages
func messages(_ callback: LibraryCallback<InAppMessage>?)
}
......@@ -328,6 +328,12 @@ class DefaultAccountProvider: AccountProvider, ConfigurationAccess, DatabaseAcce
}
}
func inAppMessages(_ callback: LibraryCallback<InAppMessage>?) {
webServices.messages { (message, error) in
callback?(message, error)
}
}
#if os(iOS)
func subscriptionInformation(_ callback: LibraryCallback<AppStoreInformation>?) {
log.debug("Fetching available product keys...")
......
......@@ -43,6 +43,13 @@ class DefaultTileProvider: TileProvider, DatabaseAccess {
}
}
// MARK: TileProvider
var fixedTiles: [AvailableTiles] {
get {
return AvailableTiles.fixedTiles()
}
}
// MARK: TileProvider
var orderedTiles: [AvailableTiles] {
get {
......
......@@ -419,6 +419,35 @@ class PIAWebServices: WebServices, ConfigurationAccess {
}
}
// MARK: Messages
func messages(_ callback: LibraryCallback<InAppMessage>?) {
if let token = Client.providers.accountProvider.token {
self.accountAPI.message(token: token, callback: { (message, error) in
if let error = error {
callback?(nil, ClientError.malformedResponseData)
return
}
if let message = message {
let inAppMessage = InAppMessage(withMessage: message, andLevel: .api)
callback?(inAppMessage, nil)
} else {
callback?(nil, nil)
}
})
} else {
callback?(nil, ClientError.unauthorized)
}
}
// MARK: Helpers
......
......@@ -309,4 +309,10 @@ public class MockAccountProvider: AccountProvider, WebServicesConsumer {
public func featureFlags(_ callback: SuccessLibraryCallback?) {
callback?(nil)
}
public func inAppMessages(_ callback: LibraryCallback<InAppMessage>?) {
delegate.inAppMessages { (message, error) in
callback?(message, error)
}
}
}
......@@ -26,6 +26,8 @@ public class MockTileProvider: TileProvider {
public var visibleTiles: [AvailableTiles] = AvailableTiles.defaultTiles()
public var fixedTiles: [AvailableTiles] = AvailableTiles.fixedTiles()
public var orderedTiles: [AvailableTiles] = AvailableTiles.defaultOrderedTiles()
/// :nodoc:
......
......@@ -24,6 +24,8 @@ import Foundation
class MockWebServices: WebServices {
var messageType: InAppMessageType = .view
var credentials: (() -> Credentials)?
var accountInfo: (() -> AccountInfo)?
......@@ -134,5 +136,12 @@ class MockWebServices: WebServices {
func featureFlags(_ callback: LibraryCallback<[String]>?) {
callback?(["mock-test"], nil)
}
func messages(_ callback: LibraryCallback<InAppMessage>?) {
let testLink = InAppMessage(withMessage: ["en" : "This is a message"], id: "1", link: ["en" : "message"], type: .link, level: .api, actions: nil, view: nil, uri: "https://www.privateinternetaccess.com")
callback?(testLink, nil)
}
}
......@@ -35,6 +35,9 @@ public extension Notification.Name {
/// Reload the tiles.
static let PIATilesDidChange = Notification.Name("PIATilesDidChange")
/// Reload the tiles with animation.
static let PIAUpdateFixedTiles = Notification.Name("PIAUpdateFixedTiles")
/// Present Recover Signup page
static let PIARecoverAccount = Notification.Name("PIARecoverAccount")
......
......@@ -275,6 +275,11 @@ public class Theme {
view.backgroundColor = palette.accent1
}
/// :nodoc:
public func applyMessagesBackground(_ view: UIView) {
view.backgroundColor = palette.appearance == .dark ? UIColor.piaGrey8 : UIColor.piaGrey2
}
// MARK: Table View Utils
/// :nodoc:
......@@ -502,6 +507,29 @@ public class Theme {
return attributed
}
public func messageWithLinkText(withMessage message: String, link: String) -> NSAttributedString {
let plain = message.replacingOccurrences(
of: "$1",
with: link
) as NSString
let attributed = NSMutableAttributedString(string: plain as String)
let paragraph = NSMutableParagraphStyle()
paragraph.alignment = .center
paragraph.minimumLineHeight = 16
let fullRange = NSMakeRange(0, plain.length)
attributed.addAttribute(.font, value: UIFont.mediumFontWith(size: 14), range: fullRange)
if Theme.current.palette.appearance == .dark {
attributed.addAttribute(.foregroundColor, value: UIColor.white, range: fullRange)
} else {
attributed.addAttribute(.foregroundColor, value: UIColor.piaGrey6, range: fullRange)
}
attributed.addAttribute(.paragraphStyle, value: paragraph, range: fullRange)
let range1 = plain.range(of: link)
attributed.addAttribute(.link, value: link, range: range1)
return attributed
}
// MARK: Composite
......@@ -585,6 +613,10 @@ public class Theme {
textView.tintColor = palette.lineColor
}
public func applyMessageLinkAttributes(_ textView: UITextView, withColor color: UIColor) {
textView.tintColor = color
}
/// :nodoc:
public func applyScrollableMap(_ imageView: UIImageView) {
imageView.image = palette.appearance == .dark ?
......
......@@ -434,4 +434,7 @@ class EphemeralAccountProvider: AccountProvider, ProvidersAccess, InAppAccess {
callback?(nil)
}
func inAppMessages(_ callback: LibraryCallback<InAppMessage>?) {
callback?(nil, nil)
}
}
......@@ -19,7 +19,7 @@ abstract_target 'PIALibrary' do
pod 'PopupDialog'
pod 'TunnelKit', :git => 'https://github.com/pia-foss/tunnelkit', :commit => 'd19b9de'
pod 'PIAWireguard', :git => "https://github.com/pia-foss/ios-wireguard"
pod "PIAAccountModule", :path => "/Users/jose/Projects/PIA/account"
pod "PIAAccountModule", :git => "https://github.com/pia-foss/mobile-common-account"
pod 'PIARegions', :git => "https://github.com/pia-foss/mobile-common-regions"
target 'PIALibrary-iOS' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment