From cd4e0e4d1e2aab1e38a6cb5e29a6103584913d0f Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 27 Mar 2024 16:21:11 -0400 Subject: [PATCH 01/22] Update header --- .../Extensions/UserText+NetworkProtectionUI.swift | 3 ++- .../TunnelControllerView/TunnelControllerView.swift | 11 ++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/UserText+NetworkProtectionUI.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/UserText+NetworkProtectionUI.swift index 402f85098f..eca9eeefd8 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/UserText+NetworkProtectionUI.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/UserText+NetworkProtectionUI.swift @@ -19,7 +19,8 @@ import Foundation final class UserText { - static let networkProtectionStatusViewFeatureDesc = NSLocalizedString("network.protection.status.view.feature.description", value: "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere.", comment: "Feature description shown in NetworkProtection's status view.") + static let networkProtectionStatusHeaderMessageOff = NSLocalizedString("network.protection.status.header.message.off", value: "Connect to secure all of your device’s\nInternet traffic.", comment: "Message label text for the status view when VPN is disconnected") + static let networkProtectionStatusHeaderMessageOn = NSLocalizedString("network.protection.status.header.message.on", value: "All device Internet traffic is being secured\nthrough the VPN.", comment: "Message label text for the status view when VPN is disconnected") static let networkProtectionStatusViewConnDetails = NSLocalizedString("network.protection.status.view.connection.details", value: "Connection Details", comment: "Connection details label shown in NetworkProtection's status view.") static let networkProtectionStatusViewConnLabel = NSLocalizedString("network.protection.status.view.connection.label", value: "VPN", comment: "Connection label shown in NetworkProtection's status view.") static let networkProtectionStatusViewLocation = NSLocalizedString("network.protection.status.view.location", value: "Location", comment: "Location label shown in NetworkProtection's status view.") diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift index 5f724d601d..ce7d20e3b6 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift @@ -175,7 +175,7 @@ public struct TunnelControllerView: View { .padding([.top], 8) .multilineText() - Text(UserText.networkProtectionStatusViewFeatureDesc) + Text(model.isToggleOn.wrappedValue ? UserText.networkProtectionStatusHeaderMessageOn : UserText.networkProtectionStatusHeaderMessageOff) .multilineText() .multilineTextAlignment(.center) .applyDescriptionAttributes(colorScheme: colorScheme) @@ -227,6 +227,8 @@ public struct TunnelControllerView: View { Spacer(minLength: 8) + statusBadge(isConnected: model.isToggleOn.wrappedValue) + Text(model.connectionStatusDescription) .applyTimerAttributes(colorScheme: colorScheme) .fixedSize() @@ -259,4 +261,11 @@ public struct TunnelControllerView: View { } .padding(EdgeInsets(top: 6, leading: 10, bottom: 6, trailing: 9)) } + + @ViewBuilder + private func statusBadge(isConnected: Bool) -> some View { + Circle() + .fill(isConnected ? .green : .yellow) + .frame(width: 8, height: 8) + } } From 3a8ebc9a8473133ce8334ee6df8763fa1d78dab9 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Tue, 2 Apr 2024 10:17:19 -0400 Subject: [PATCH 02/22] Status badge --- .../TunnelControllerView.swift | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift index ce7d20e3b6..b5ed4b07a1 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift @@ -184,6 +184,13 @@ public struct TunnelControllerView: View { } } + @ViewBuilder + private func statusBadge(isConnected: Bool) -> some View { + Circle() + .fill(isConnected ? .green : .yellow) + .frame(width: 8, height: 8) + } + /// Connection status: server IP address and location /// private func connectionStatusView() -> some View { @@ -261,11 +268,5 @@ public struct TunnelControllerView: View { } .padding(EdgeInsets(top: 6, leading: 10, bottom: 6, trailing: 9)) } - - @ViewBuilder - private func statusBadge(isConnected: Bool) -> some View { - Circle() - .fill(isConnected ? .green : .yellow) - .frame(width: 8, height: 8) - } } + From bf32d192ceced253fb67025ca6e664613d22794b Mon Sep 17 00:00:00 2001 From: Anh Do Date: Tue, 2 Apr 2024 20:48:39 -0400 Subject: [PATCH 03/22] New assets --- DuckDuckGo.xcodeproj/project.pbxproj | 17 ++++++++++++++++- DuckDuckGo/vpn-dark-mode.json | 1 + DuckDuckGo/vpn-light-mode.json | 1 + .../NetworkProtectionAsset.swift | 4 ++-- .../Network-Protetion-VPN-128.pdf | Bin 14844 -> 0 bytes .../Network-Protetion-VPN-Disabled-128.pdf | Bin 14855 -> 0 bytes .../Contents.json | 2 +- .../Assets.xcassets/Icons/VPN.imageset/VPN.pdf | Bin 0 -> 14915 bytes .../Contents.json | 2 +- .../Icons/VPNDisabled.imageset/VPNDisabled.pdf | Bin 0 -> 14932 bytes .../Icons/VPNDownload.imageset/Contents.json | 15 +++++++++++++++ .../VPNDownload.imageset/vpn-download.pdf | Bin 0 -> 1357 bytes .../Icons/VPNLocation.imageset/Contents.json | 15 +++++++++++++++ .../Icons/VPNLocation.imageset/VPNLocation.pdf | Bin 0 -> 1497 bytes .../Icons/VPNUpload.imageset/Contents.json | 15 +++++++++++++++ .../Icons/VPNUpload.imageset/vpn-upload.pdf | Bin 0 -> 1361 bytes .../TunnelControllerView.swift | 15 +++++++++++---- 17 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 DuckDuckGo/vpn-dark-mode.json create mode 100644 DuckDuckGo/vpn-light-mode.json delete mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPN-128.imageset/Network-Protetion-VPN-128.pdf delete mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPN-Disabled-128.imageset/Network-Protetion-VPN-Disabled-128.pdf rename LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/{VPN-128.imageset => VPN.imageset}/Contents.json (70%) create mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPN.imageset/VPN.pdf rename LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/{VPN-Disabled-128.imageset => VPNDisabled.imageset}/Contents.json (66%) create mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNDisabled.imageset/VPNDisabled.pdf create mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNDownload.imageset/Contents.json create mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNDownload.imageset/vpn-download.pdf create mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNLocation.imageset/Contents.json create mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNLocation.imageset/VPNLocation.pdf create mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNUpload.imageset/Contents.json create mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNUpload.imageset/vpn-upload.pdf diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 2c93efafdd..fc174b22a8 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3207,6 +3207,12 @@ BBDFDC5A2B2B8A0900F62D90 /* DataBrokerProtectionExternalWaitlistPixels.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBDFDC592B2B8A0900F62D90 /* DataBrokerProtectionExternalWaitlistPixels.swift */; }; BBDFDC5C2B2B8D7000F62D90 /* DataBrokerProtectionExternalWaitlistPixels.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBDFDC592B2B8A0900F62D90 /* DataBrokerProtectionExternalWaitlistPixels.swift */; }; BBDFDC5D2B2B8E2100F62D90 /* DataBrokerProtectionExternalWaitlistPixels.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBDFDC592B2B8A0900F62D90 /* DataBrokerProtectionExternalWaitlistPixels.swift */; }; + BD384AC92BBC821A00EF3735 /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; + BD384ACA2BBC821A00EF3735 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; + BD384ACB2BBC821B00EF3735 /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; + BD384ACC2BBC821B00EF3735 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; + BD384ACD2BBC821D00EF3735 /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; + BD384ACE2BBC821D00EF3735 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; C13909EF2B85FD4E001626ED /* AutofillActionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */; }; C13909F02B85FD4E001626ED /* AutofillActionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */; }; C13909F12B85FD4E001626ED /* AutofillActionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */; }; @@ -4700,6 +4706,8 @@ B6FA8940269C425400588ECD /* PrivacyDashboardPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyDashboardPopover.swift; sourceTree = ""; }; BB5789712B2CA70F0009DFE2 /* DataBrokerProtectionSubscriptionEventHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionSubscriptionEventHandler.swift; sourceTree = ""; }; BBDFDC592B2B8A0900F62D90 /* DataBrokerProtectionExternalWaitlistPixels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionExternalWaitlistPixels.swift; sourceTree = ""; }; + BD384AC72BBC821100EF3735 /* vpn-light-mode.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "vpn-light-mode.json"; sourceTree = ""; }; + BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "vpn-dark-mode.json"; sourceTree = ""; }; C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillActionExecutor.swift; sourceTree = ""; }; C13909F32B85FD79001626ED /* AutofillDeleteAllPasswordsExecutorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillDeleteAllPasswordsExecutorTests.swift; sourceTree = ""; }; C13909FA2B861039001626ED /* AutofillActionPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillActionPresenter.swift; sourceTree = ""; }; @@ -7165,6 +7173,8 @@ children = ( AA4D700525545EDE00C3411E /* Application */, AA585D85248FD31400E9A3E2 /* Assets.xcassets */, + BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */, + BD384AC72BBC821100EF3735 /* vpn-light-mode.json */, B31055BB27A1BA0E001AC618 /* Autoconsent */, 7B1E819A27C8874900FF0E60 /* Autofill */, AAC5E4C025D6A6A9007F5990 /* Bookmarks */, @@ -9555,7 +9565,9 @@ 3706FCE6293F65D500E42796 /* social_images in Resources */, 3706FCE7293F65D500E42796 /* shield-dot-mouse-over.json in Resources */, 3706FCE9293F65D500E42796 /* fb-sdk.js in Resources */, + BD384ACB2BBC821B00EF3735 /* vpn-dark-mode.json in Resources */, 3706FCEA293F65D500E42796 /* PasswordManager.storyboard in Resources */, + BD384ACC2BBC821B00EF3735 /* vpn-light-mode.json in Resources */, 3706FCEB293F65D500E42796 /* dark-flame-mouse-over.json in Resources */, 3706FCEC293F65D500E42796 /* flame-mouse-over.json in Resources */, 3706FCED293F65D500E42796 /* httpsMobileV2Bloom.bin in Resources */, @@ -9682,7 +9694,9 @@ 4B957C1F2AC7AE700062CA31 /* social_images in Resources */, 4B957C202AC7AE700062CA31 /* shield-dot-mouse-over.json in Resources */, 4B957C222AC7AE700062CA31 /* fb-sdk.js in Resources */, + BD384ACD2BBC821D00EF3735 /* vpn-dark-mode.json in Resources */, 4B957C232AC7AE700062CA31 /* PasswordManager.storyboard in Resources */, + BD384ACE2BBC821D00EF3735 /* vpn-light-mode.json in Resources */, 4B957C242AC7AE700062CA31 /* dark-flame-mouse-over.json in Resources */, 4B957C252AC7AE700062CA31 /* flame-mouse-over.json in Resources */, 4B957C262AC7AE700062CA31 /* httpsMobileV2Bloom.bin in Resources */, @@ -9798,7 +9812,9 @@ EA18D1CA272F0DC8006DC101 /* social_images in Resources */, AA7EB6E927E880A600036718 /* shield-dot-mouse-over.json in Resources */, EAC80DE0271F6C0100BBF02D /* fb-sdk.js in Resources */, + BD384AC92BBC821A00EF3735 /* vpn-dark-mode.json in Resources */, 85625994269C8F9600EE44BC /* PasswordManager.storyboard in Resources */, + BD384ACA2BBC821A00EF3735 /* vpn-light-mode.json in Resources */, AA7EB6E327E7D05500036718 /* dark-flame-mouse-over.json in Resources */, AA7EB6E227E7D05500036718 /* flame-mouse-over.json in Resources */, 4B677433255DBEB800025BD8 /* httpsMobileV2Bloom.bin in Resources */, @@ -14476,7 +14492,6 @@ }; 3706FA6B293F65D500E42796 /* TrackerRadarKit */ = { isa = XCSwiftPackageProductDependency; - package = 3706FA6C293F65D500E42796 /* XCRemoteSwiftPackageReference "TrackerRadarKit" */; productName = TrackerRadarKit; }; 3706FA71293F65D500E42796 /* BrowserServicesKit */ = { diff --git a/DuckDuckGo/vpn-dark-mode.json b/DuckDuckGo/vpn-dark-mode.json new file mode 100644 index 0000000000..2b97395d0e --- /dev/null +++ b/DuckDuckGo/vpn-dark-mode.json @@ -0,0 +1 @@ +{"v":"5.7.5","fr":100,"ip":0,"op":370,"w":128,"h":96,"nm":"Comp 1","ddd":0,"assets":[{"id":"0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"shadow","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[42.2357292175293,8.99384593963623],[41.2205810546875,0],[53.23579788208008,24.99367523193359],[21.23579597473145,56.99367141723633],[0,48.93241500854492],[2.235732555389404,48.99384307861328],[42.2357292175293,8.99384593963623]],"i":[[0,0],[0.664215087890625,2.89088249206543],[0,-10.11254501342773],[17.6731128692627,0],[5.649600028991699,5.015373229980469],[-0.750247597694397,0],[0,22.09139251708984]],"o":[[0,-3.092463970184326],[7.324420928955078,5.864182472229004],[0,17.6731128692627],[-8.148801803588867,0],[0.7400614619255066,0.040771484375],[22.09139251708984,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.16862745098039217,0.3333333333333333,0.792156862745098],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[69.3820571899414,35.50293731689453],"ix":2},"a":{"a":0,"k":[26.61789894104004,28.49683570861816],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":1,"k":[{"t":40,"s":[40],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[50],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"dot-nw","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"el","d":1,"s":{"a":0,"k":[7,7],"ix":2},"p":{"a":0,"k":[0,0],"ix":2}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":10,"s":[46.5,13.5],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[50.5,6.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-14.74848774275709,-23.38554600445718],"to":[-12.74848774275709,16.61445399554282]},{"t":80,"s":[53.5,66.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":81,"s":[57.5,-2.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":200,"s":[57.5,-2.5],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-20.75,-24.25],"to":[-20.75,23.08333333333333]},{"t":370,"s":[56.5,68.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"dot-ne","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"el","d":1,"s":{"a":0,"k":[7,7],"ix":2},"p":{"a":0,"k":[0,0],"ix":2}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":10,"s":[69.5,21.5],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[63.5,17.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-6.951289398280807,-10.83253740846864],"to":[9.048710601719193,4.500795924864676]},{"t":80,"s":[87.5,40.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[26.75000000000003,-1.75],"to":[-10.58333333333334,-19.75]},{"t":81,"s":[31.5,13.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":150,"s":[31.5,13.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-9.750000000000028,-19.41666666666666],"to":[27.58333333333334,-1.416666666666664]},{"t":300,"s":[87.5,40.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"dot-s","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"el","d":1,"s":{"a":0,"k":[7,7],"ix":2},"p":{"a":0,"k":[0,0],"ix":2}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":10,"s":[59.5,49.5],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[69.5,50.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[10.97134670487107,11.24832855778413],"to":[-17.69531996179562,-0.08500477554918007]},{"t":80,"s":[26.5,31.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-20.749999999999986,-1.75],"to":[10.58333333333333,11.58333333333333]},{"t":81,"s":[73.5,52.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":130,"s":[73.5,50.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[12.58333333333336,13.250000000000014],"to":[-18.75,0.5833333333333144]},{"t":230,"s":[26.5,32.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[10056,10032.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[17,30.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[-34,-13.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[9,-30.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[-13,28.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[34,0.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"globe","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[32,63.99999999999999],[0,32],[32,0],[63.99999999999999,32],[32,63.99999999999999]],"i":[[0,0],[0,17.67311330810018],[-17.67311330810018,0],[0,-17.67311330810018],[17.67311330810018,0]],"o":[[-17.67311330810018,0],[0,-17.67311330810018],[17.67311330810018,0],[0,17.67311330810018],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.8,0.8,0.8],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.3333333333333333,0.4980392156862745,0.9529411764705882],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[64,32],"ix":2},"a":{"a":0,"k":[32,32],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0}]},{"id":"1","layers":[{"ddd":0,"ind":12,"ty":4,"nm":"_mask","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"td":1,"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[55,35],[35,55],[35,61.1272168529097],[32.27027277742516,63.99888239316815],[32,64],[0,32],[32,0],[64,32],[63.99888239502384,32.27027632020794],[61.12722036453148,35],[55,35]],"i":[[0,0],[1.3527074874795e-15,-11.04569435119629],[0,-2.603342616466307],[1.549197989173602,-0.01281636670575817],[0.09017852706661245,0],[-2.164332234077084e-15,17.6731128692627],[-17.6731128692627,-2.164332234077084e-15],[2.164332445835321e-15,-17.67311477661133],[0.0007445845155729103,-0.09000398946863442],[1.549249531796702,0],[0,0]],"o":[[-11.04569244384766,-1.352707275721263e-15],[0,0],[0,1.549251002554159],[-0.09000283819779753,0.0007445848671139288],[-17.6731128692627,-2.164332234077084e-15],[2.164332022318847e-15,-17.67311096191406],[17.67311096191406,2.164332022318847e-15],[0,0.09017973898513532],[-0.01281618456122568,1.549196519872671],[-2.603342959208661,0],[0,0]]}}},{"ty":"fl","c":{"a":0,"k":[0,0,0],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[64,32],"ix":2},"a":{"a":0,"k":[32,32],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"refId":"0","w":20000,"h":20000,"ind":13,"ty":0,"nm":"globe","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"tt":1}]}],"layers":[{"ddd":0,"ind":14,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[64,48],"ix":2},"a":{"a":0,"k":[64,35],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":15,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":1,"k":[{"t":10,"s":[87,54],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":30,"s":[87,48],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":40,"s":[87,58],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":50,"s":[87,54],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[87,54],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":14},{"ddd":0,"ind":16,"ty":4,"nm":"lock-hook","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":15,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":false,"v":[[0,9],[0,4.048697948455811],[1.171572897169325,1.185836362838745],[4,2.333394250525001e-14],[6.828427208794487,1.185835719108582],[8,4.048697519302368],[8,8.46090259552002]],"i":[[0,0],[0,0],[-0.7501454883151584,0.7592780649662018],[-1.060865932040744,1.151999967419215e-7],[-0.7501456472608777,-0.7592779576778412],[-3.203726414034867e-7,-1.073781502246857],[0,0]],"o":[[0,0],[9.274384638047195e-8,-1.073781502246857],[0.7501454883151584,-0.7592780649662018],[1.060865932040744,-1.151999967419215e-7],[0.7501456472608777,0.7592779576778412],[0,0],[0,0]]}}},{"ty":"st","c":{"a":0,"k":[0.06666666666666667,0.06666666666666667,0.06666666666666667],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2.666666746139526,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":1,"k":[{"t":30,"s":[85],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[87,50.5],"ix":2},"a":{"a":0,"k":[4,4.5],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":1,"k":[{"t":30,"s":[196],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":-180,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"lock-base","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":15,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[2.000000000000001,0],[14,0],[16,2.000000000000002],[16,7.333333015441899],[14,9.333333015441895],[1.999999999999996,9.333333015441895],[0,7.333333015441892],[0,1.999999999999995],[2.000000000000001,0]],"i":[[0,0],[-3.074004826695607,0],[0,-1.104569499661587],[0,-1.574570024376729],[1.104569499661587,0],[3.074004826695605,0],[0,1.104569499661587],[0,1.574570024376728],[-1.104569499661587,0]],"o":[[3.074004826695607,0],[1.104569499661587,0],[0,1.57457002437673],[0,1.104569499661587],[-3.074004826695608,0],[-1.104569499661587,0],[0,-1.57457002437673],[0,-1.104569499661587],[0,0]]}}},{"ty":"fl","c":{"a":0,"k":[0.06666666666666667,0.06666666666666667,0.06666666666666667],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[87,58.66666793823242],"ix":2},"a":{"a":0,"k":[8,4.666666507720947],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":180,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":-180,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"bg","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":15,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[32,16],[16,32],[0,16],[16,0],[32,16]],"i":[[0,0],[8.836555727066532,0],[0,8.836555727066532],[-8.836555727066532,0],[0,-8.836555727066532]],"o":[[0,8.836555727066532],[-8.836555727066532,0],[0,-8.836555727066532],[8.836555727066532,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":30,"s":[0.9764705882352941,0.7450980392156863,0.10196078431372549],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[0.2980392156862745,0.7294117647058823,0.23529411764705882],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[87,54],"ix":2},"a":{"a":0,"k":[16,16],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"refId":"1","w":20000,"h":20000,"ind":13,"ty":0,"nm":"globe","sr":1,"ks":{"p":{"a":0,"k":[64,32],"ix":2},"a":{"a":0,"k":[10064,10032],"ix":2},"s":{"a":1,"k":[{"t":40,"s":[100,100],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":60,"s":[85,85],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":70,"s":[105,105],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":80,"s":[100,100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":14},{"ddd":0,"ind":19,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[64,38.42728805541992],"ix":2},"a":{"a":0,"k":[64,38.42728805541992],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":14},{"ddd":0,"ind":20,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[108.5400009155273,55],"ix":2},"a":{"a":0,"k":[108.5400009155273,55.00000190734863],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":19},{"ddd":0,"ind":21,"ty":4,"nm":"bling-N","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":20,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":false,"v":[[0,0],[3.329999923706055,0]],"i":[[0,0],[0,0]],"o":[[0,0],[0,0]]}}},{"ty":"st","c":{"a":0,"k":[0.8,0.8,0.8],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2.5,"ix":2},"lc":2,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":1,"k":[{"t":40,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":47,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":174,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":176,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":183,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[107.4273300170898,45.32266616821289],"ix":2},"a":{"a":0,"k":[1.664999961853027,0],"ix":2},"s":{"a":0,"k":[99.9999982885729,99.99999828857288],"ix":2},"r":{"a":0,"k":-45,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"bling","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":20,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":false,"v":[[0,0],[3.329999923706055,0]],"i":[[0,0],[0,0]],"o":[[0,0],[0,0]]}}},{"ty":"st","c":{"a":0,"k":[0.8,0.8,0.8],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2.5,"ix":2},"lc":2,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":1,"k":[{"t":47,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":53,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":175,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":176,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":183,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":189,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[109.1650009155273,55],"ix":2},"a":{"a":0,"k":[1.664999961853027,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":"bling-S","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":20,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":false,"v":[[0,0],[3.329999923706055,0]],"i":[[0,0],[0,0]],"o":[[0,0],[0,0]]}}},{"ty":"st","c":{"a":0,"k":[0.8,0.8,0.8],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2.5,"ix":2},"lc":2,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":1,"k":[{"t":53,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":60,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":175,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":176,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":189,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":190,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":196,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[107.4273300170898,64.67733001708984],"ix":2},"a":{"a":0,"k":[1.664999961853027,0],"ix":2},"s":{"a":0,"k":[99.9999982885729,99.99999828857288],"ix":2},"r":{"a":0,"k":45,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":24,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[64,45.49990844726562],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":19},{"ddd":0,"ind":25,"ty":4,"nm":"dot-L","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[2,4],[0,2],[2,0],[4,2],[2,4]],"i":[[0,0],[0,1.100000023841858],[-1.100000023841858,0],[0,-1.100000023841858],[1.100000023841858,0]],"o":[[-1.100000023841858,0],[0,-1.100000023841858],[1.100000023841858,0],[0,1.100000023841858],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.10196078431372549],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[-1,-15.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[-41,6.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[2,2],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":"dot-R","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[1.5,3],[0,1.5],[1.5,0],[3,1.5],[1.5,3]],"i":[[0,0],[0,0.8250000178813934],[-0.8250000178813934,0],[0,-0.8250000178813934],[0.8250000178813934,0]],"o":[[-0.8250000178813934,0],[0,-0.8250000178813934],[0.8250000178813934,0],[0,0.8250000178813934],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.10196078431372549],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[3.5,-14],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[62.5,-11],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[62.5,-11],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[1.5,1.5],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":27,"ty":4,"nm":"spark-L","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[7.371442522321428,3.199999128069196],[8,4],[7.371442522321428,4.800000871930803],[5.828578404017857,5.199999128069196],[5.199986049107142,5.828569684709821],[4.800013950892857,7.371429443359375],[4,8],[3.199986049107143,7.371429443359375],[2.800013950892857,5.828569684709821],[2.171421595982143,5.199999128069196],[0.6285574776785714,4.800000871930803],[0,4],[0.6285574776785714,3.199999128069196],[2.171421595982143,2.800000871930803],[2.800013950892857,2.171430315290178],[3.199986049107143,0.628570556640625],[4,0],[4.800013950892857,0.628570556640625],[5.199986049107142,2.171430315290178],[5.828578404017857,2.800000871930803],[7.371442522321428,3.199999128069196]],"i":[[0,0],[0,-0.3999999931880406],[0.3999999931880406,-0.1142857159887041],[0,0],[0.05714285799435206,-0.2857142857142857],[0,0],[0.3999999931880406,0],[0.1142857159887041,0.3999999931880406],[0,0],[0.2857142857142857,0.05714285799435206],[0,0],[0,0.3999999931880406],[-0.3999999931880406,0.1142857159887041],[0,0],[-0.05714285799435206,0.2857142857142857],[0,0],[-0.3999999931880406,0],[-0.1142857159887041,-0.3999999931880406],[0,0],[-0.2857142857142857,-0.05714285799435206],[0,0]],"o":[[0.3428571564810616,0.05714285799435206],[0,0.3428571564810616],[0,0],[-0.2857142857142857,0.05714285799435206],[0,0],[-0.05714285799435206,0.3428571564810616],[-0.3428571564810616,0],[0,0],[-0.05714285799435206,-0.2857142857142857],[0,0],[-0.3428571564810616,-0.05714285799435206],[0,-0.3428571564810616],[0,0],[0.2857142857142857,-0.05714285799435206],[0,0],[0.05714285799435206,-0.3428571564810616],[0.3428571564810616,0],[0,0],[0.05714285799435206,0.2857142857142857],[0,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.10196078431372549],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[-1,-16.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[-57,-7.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[4,4],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":1,"k":[{"t":40,"s":[360],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":130,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":370,"s":[-180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"spark-Big","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[11.05716378348214,4.799997929164549],[12,5.999999046325684],[11.05716378348214,7.200000163486817],[8.742867606026785,7.799997452327391],[7.799979073660714,8.74285313742543],[7.200020926339286,11.05714240755333],[6,11.99999809265137],[4.799979073660714,11.05714240755333],[4.200020926339286,8.74285313742543],[3.257132393973214,7.799997452327391],[0.9428362165178571,7.200000163486817],[0,5.999999046325684],[0.9428362165178571,4.799997929164549],[3.257132393973214,4.200000640323976],[4.200020926339286,3.257144955225937],[4.799979073660714,0.9428556850980385],[6,0],[7.200020926339286,0.9428556850980385],[7.799979073660714,3.257144955225937],[8.742867606026785,4.200000640323976],[11.05716378348214,4.799997929164549]],"i":[[0,0],[0,-0.5999998944146309],[0.5999999897820608,-0.1714285467352182],[0,0],[0.0857142869915281,-0.4285713604518345],[0,0],[0.5999999897820608,0],[0.1714285739830562,0.5999998944146309],[0,0],[0.4285714285714285,0.08571427336760909],[0,0],[0,0.5999998944146309],[-0.5999999897820608,0.1714285467352182],[0,0],[-0.0857142869915281,0.4285713604518345],[0,0],[-0.5999999897820608,0],[-0.1714285739830562,-0.5999998944146309],[0,0],[-0.4285714285714285,-0.08571427336760909],[0,0]],"o":[[0.5142857347215924,0.08571427336760909],[0,0.5142856529780764],[0,0],[-0.4285714285714285,0.08571427336760909],[0,0],[-0.0857142869915281,0.5142856529780764],[-0.5142857347215924,0],[0,0],[-0.0857142869915281,-0.4285713604518345],[0,0],[-0.5142857347215924,-0.08571427336760909],[0,-0.5142856529780764],[0,0],[0.4285714285714285,-0.08571427336760909],[0,0],[0.0857142869915281,-0.5142856529780764],[0.5142857347215924,0],[0,0],[0.0857142869915281,0.4285713604518345],[0,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.10196078431372549],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[-1,-15.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[-43,-28.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[-43,-28.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[6,5.999999046325684],"ix":2},"s":{"a":1,"k":[{"t":130,"s":[100,-100],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":250,"s":[50,-50],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":370,"s":[100,-100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":{"a":1,"k":[{"t":40,"s":[360],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":29,"ty":4,"nm":"spark-R","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[7.371442794799805,3.199999173482259],[8,4],[7.371442794799805,4.80000082651774],[5.828578313191731,5.199999173482259],[5.199986139933268,5.828570048014322],[4.800013860066731,7.371429443359375],[4,8],[3.199986139933268,7.371429443359375],[2.800013860066731,5.828570048014322],[2.171421686808268,5.199999173482259],[0.6285574833552042,4.80000082651774],[0,4],[0.6285574833552042,3.199999173482259],[2.171421686808268,2.800000826517741],[2.800013860066731,2.171430269877116],[3.199986139933268,0.628570556640625],[4,0],[4.800013860066731,0.628570556640625],[5.199986139933268,2.171430269877116],[5.828578313191731,2.800000826517741],[7.371442794799805,3.199999173482259]],"i":[[0,0],[0,-0.4000000158945719],[0.4000000158945719,-0.1142857174078623],[0,0],[0.05714285870393117,-0.2857142885526021],[0,0],[0.4000000158945719,0],[0.1142857174078623,0.4000000158945719],[0,0],[0.2857142885526021,0.05714285870393117],[0,0],[0,0.4000000158945719],[-0.4000000158945719,0.1142857174078623],[0,0],[-0.05714285870393117,0.2857142885526021],[0,0],[-0.4000000158945719,0],[-0.1142857174078623,-0.4000000158945719],[0,0],[-0.2857142885526021,-0.05714285870393117],[0,0]],"o":[[0.3428571621576945,0.05714285870393117],[0,0.3428571621576945],[0,0],[-0.2857142885526021,0.05714285870393117],[0,0],[-0.05714285870393117,0.3428571621576945],[-0.3428571621576945,0],[0,0],[-0.05714285870393117,-0.2857142885526021],[0,0],[-0.3428571621576945,-0.05714285870393117],[0,-0.3428571621576945],[0,0],[0.2857142885526021,-0.05714285870393117],[0,0],[0.05714285870393117,-0.3428571621576945],[0.3428571621576945,0],[0,0],[0.05714285870393117,0.2857142885526021],[0,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.10196078431372549],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[2,-14.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[45,-22.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[45,-22.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[4,4],"ix":2},"s":{"a":1,"k":[{"t":130,"s":[100,-100],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":250,"s":[250,-250],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":370,"s":[100,-100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":{"a":1,"k":[{"t":40,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/DuckDuckGo/vpn-light-mode.json b/DuckDuckGo/vpn-light-mode.json new file mode 100644 index 0000000000..bd6adbe2e8 --- /dev/null +++ b/DuckDuckGo/vpn-light-mode.json @@ -0,0 +1 @@ +{"v":"5.7.5","fr":100,"ip":0,"op":370,"w":128,"h":96,"nm":"Comp1","ddd":0,"assets":[{"id":"0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"shadow","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[42.2357292175293,8.99384593963623],[41.2205810546875,0],[53.23579788208008,24.99367523193359],[21.23579597473145,56.99367141723633],[0,48.93241500854492],[2.235732555389404,48.99384307861328],[42.2357292175293,8.99384593963623]],"i":[[0,0],[0.664215087890625,2.89088249206543],[0,-10.11254501342773],[17.6731128692627,0],[5.649600028991699,5.015373229980469],[-0.750247597694397,0],[0,22.09139251708984]],"o":[[0,-3.092463970184326],[7.324420928955078,5.864182472229004],[0,17.6731128692627],[-8.148801803588867,0],[0.7400614619255066,0.040771484375],[22.09139251708984,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.1686274509803922,0.3333333333333333,0.792156862745098],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[69.3820571899414,35.50293731689453],"ix":2},"a":{"a":0,"k":[26.61789894104004,28.49683570861816],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":1,"k":[{"t":40,"s":[40],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[50],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"dot-nw","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"el","d":1,"s":{"a":0,"k":[7,7],"ix":2},"p":{"a":0,"k":[0,0],"ix":2}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":10,"s":[46.5,13.5],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[50.5,6.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-14.74848774275709,-23.38554600445718],"to":[-12.74848774275709,16.61445399554282]},{"t":80,"s":[53.5,66.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":81,"s":[57.5,-2.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":200,"s":[57.5,-2.5],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-20.75,-24.25],"to":[-20.75,23.08333333333333]},{"t":370,"s":[56.5,68.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"dot-ne","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"el","d":1,"s":{"a":0,"k":[7,7],"ix":2},"p":{"a":0,"k":[0,0],"ix":2}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":10,"s":[69.5,21.5],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[63.5,17.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-6.951289398280807,-10.83253740846864],"to":[9.048710601719193,4.500795924864676]},{"t":80,"s":[87.5,40.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[26.75000000000003,-1.75],"to":[-10.58333333333334,-19.75]},{"t":81,"s":[31.5,13.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":150,"s":[31.5,13.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-9.750000000000028,-19.41666666666666],"to":[27.58333333333334,-1.416666666666664]},{"t":300,"s":[87.5,40.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"dot-s","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"el","d":1,"s":{"a":0,"k":[7,7],"ix":2},"p":{"a":0,"k":[0,0],"ix":2}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":10,"s":[59.5,49.5],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[69.5,50.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[10.97134670487107,11.24832855778413],"to":[-17.69531996179562,-0.08500477554918007]},{"t":80,"s":[26.5,31.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-20.749999999999986,-1.75],"to":[10.58333333333333,11.58333333333333]},{"t":81,"s":[73.5,52.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":130,"s":[73.5,50.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[12.58333333333336,13.250000000000014],"to":[-18.75,0.5833333333333144]},{"t":230,"s":[26.5,32.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[10056,10032.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[17,30.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[-34,-13.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[9,-30.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[-13,28.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[34,0.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"globe","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[32,63.99999999999999],[0,32],[32,0],[63.99999999999999,32],[32,63.99999999999999]],"i":[[0,0],[0,17.67311330810018],[-17.67311330810018,0],[0,-17.67311330810018],[17.67311330810018,0]],"o":[[-17.67311330810018,0],[0,-17.67311330810018],[17.67311330810018,0],[0,17.67311330810018],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.8,0.8,0.8],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.3333333333333333,0.4980392156862745,0.9529411764705882],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[64,32],"ix":2},"a":{"a":0,"k":[32,32],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0}]},{"id":"1","layers":[{"ddd":0,"ind":12,"ty":4,"nm":"_mask","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"td":1,"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[55,35],[35,55],[35,61.1272168529097],[32.27027277742516,63.99888239316815],[32,64],[0,32],[32,0],[64,32],[63.99888239502384,32.27027632020794],[61.12722036453148,35],[55,35]],"i":[[0,0],[1.3527074874795e-15,-11.04569435119629],[0,-2.603342616466307],[1.549197989173602,-0.01281636670575817],[0.09017852706661245,0],[-2.164332234077084e-15,17.6731128692627],[-17.6731128692627,-2.164332234077084e-15],[2.164332445835321e-15,-17.67311477661133],[0.0007445845155729103,-0.09000398946863442],[1.549249531796702,0],[0,0]],"o":[[-11.04569244384766,-1.352707275721263e-15],[0,0],[0,1.549251002554159],[-0.09000283819779753,0.0007445848671139288],[-17.6731128692627,-2.164332234077084e-15],[2.164332022318847e-15,-17.67311096191406],[17.67311096191406,2.164332022318847e-15],[0,0.09017973898513532],[-0.01281618456122568,1.549196519872671],[-2.603342959208661,0],[0,0]]}}},{"ty":"fl","c":{"a":0,"k":[0,0,0],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[64,32],"ix":2},"a":{"a":0,"k":[32,32],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"refId":"0","w":20000,"h":20000,"ind":13,"ty":0,"nm":"globe","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"tt":1}]}],"layers":[{"ddd":0,"ind":14,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[64,48],"ix":2},"a":{"a":0,"k":[64,35],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":15,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":1,"k":[{"t":10,"s":[87,54],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":30,"s":[87,48],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":40,"s":[87,58],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":50,"s":[87,54],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[87,54],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":14},{"ddd":0,"ind":16,"ty":4,"nm":"lock-hook","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":15,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":false,"v":[[0,9],[0,4.048697948455811],[1.171572897169325,1.185836362838745],[4,2.333394250525001e-14],[6.828427208794487,1.185835719108582],[8,4.048697519302368],[8,8.46090259552002]],"i":[[0,0],[0,0],[-0.7501454883151584,0.7592780649662018],[-1.060865932040744,1.151999967419215e-7],[-0.7501456472608777,-0.7592779576778412],[-3.203726414034867e-7,-1.073781502246857],[0,0]],"o":[[0,0],[9.274384638047195e-8,-1.073781502246857],[0.7501454883151584,-0.7592780649662018],[1.060865932040744,-1.151999967419215e-7],[0.7501456472608777,0.7592779576778412],[0,0],[0,0]]}}},{"ty":"st","c":{"a":0,"k":[1,1,1],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2.666666746139526,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":1,"k":[{"t":30,"s":[85],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[87,50.5],"ix":2},"a":{"a":0,"k":[4,4.5],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":1,"k":[{"t":30,"s":[196],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":-180,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"lock-base","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":15,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[2.000000000000001,0],[14,0],[16,2.000000000000002],[16,7.333333015441899],[14,9.333333015441895],[1.999999999999996,9.333333015441895],[0,7.333333015441892],[0,1.999999999999995],[2.000000000000001,0]],"i":[[0,0],[-3.074004826695607,0],[0,-1.104569499661587],[0,-1.574570024376729],[1.104569499661587,0],[3.074004826695605,0],[0,1.104569499661587],[0,1.574570024376728],[-1.104569499661587,0]],"o":[[3.074004826695607,0],[1.104569499661587,0],[0,1.57457002437673],[0,1.104569499661587],[-3.074004826695608,0],[-1.104569499661587,0],[0,-1.57457002437673],[0,-1.104569499661587],[0,0]]}}},{"ty":"fl","c":{"a":0,"k":[1,1,1],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[87,58.66666793823242],"ix":2},"a":{"a":0,"k":[8,4.666666507720947],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":180,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":-180,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"bg","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":15,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[32,16],[16,32],[0,16],[16,0],[32,16]],"i":[[0,0],[8.836555727066532,0],[0,8.836555727066532],[-8.836555727066532,0],[0,-8.836555727066532]],"o":[[0,8.836555727066532],[-8.836555727066532,0],[0,-8.836555727066532],[8.836555727066532,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":30,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[0.2980392156862745,0.7294117647058823,0.2352941176470588],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[87,54],"ix":2},"a":{"a":0,"k":[16,16],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"refId":"1","w":20000,"h":20000,"ind":13,"ty":0,"nm":"globe","sr":1,"ks":{"p":{"a":0,"k":[64,32],"ix":2},"a":{"a":0,"k":[10064,10032],"ix":2},"s":{"a":1,"k":[{"t":40,"s":[100,100],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":60,"s":[85,85],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":70,"s":[105,105],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":80,"s":[100,100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":14},{"ddd":0,"ind":19,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[64,38.42728805541992],"ix":2},"a":{"a":0,"k":[64,38.42728805541992],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":14},{"ddd":0,"ind":20,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[108.5400009155273,55],"ix":2},"a":{"a":0,"k":[108.5400009155273,55.00000190734863],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":19},{"ddd":0,"ind":21,"ty":4,"nm":"bling-N","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":20,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":false,"v":[[0,0],[3.329999923706055,0]],"i":[[0,0],[0,0]],"o":[[0,0],[0,0]]}}},{"ty":"st","c":{"a":0,"k":[0.8,0.8,0.8],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2.5,"ix":2},"lc":2,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":1,"k":[{"t":40,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":47,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":174,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":176,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":183,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[107.4273300170898,45.32266616821289],"ix":2},"a":{"a":0,"k":[1.664999961853027,0],"ix":2},"s":{"a":0,"k":[99.9999982885729,99.99999828857288],"ix":2},"r":{"a":0,"k":-45,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"bling","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":20,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":false,"v":[[0,0],[3.329999923706055,0]],"i":[[0,0],[0,0]],"o":[[0,0],[0,0]]}}},{"ty":"st","c":{"a":0,"k":[0.8,0.8,0.8],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2.5,"ix":2},"lc":2,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":1,"k":[{"t":47,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":53,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":175,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":176,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":183,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":189,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[109.1650009155273,55],"ix":2},"a":{"a":0,"k":[1.664999961853027,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":"bling-S","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":20,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":false,"v":[[0,0],[3.329999923706055,0]],"i":[[0,0],[0,0]],"o":[[0,0],[0,0]]}}},{"ty":"st","c":{"a":0,"k":[0.8,0.8,0.8],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2.5,"ix":2},"lc":2,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":1,"k":[{"t":53,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":60,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":175,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":176,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":189,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":190,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":196,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[107.4273300170898,64.67733001708984],"ix":2},"a":{"a":0,"k":[1.664999961853027,0],"ix":2},"s":{"a":0,"k":[99.9999982885729,99.99999828857288],"ix":2},"r":{"a":0,"k":45,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":24,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[64,45.49990844726562],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":19},{"ddd":0,"ind":25,"ty":4,"nm":"dot-L","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[2,4],[0,2],[2,0],[4,2],[2,4]],"i":[[0,0],[0,1.100000023841858],[-1.100000023841858,0],[0,-1.100000023841858],[1.100000023841858,0]],"o":[[-1.100000023841858,0],[0,-1.100000023841858],[1.100000023841858,0],[0,1.100000023841858],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[-1,-15.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[-41,6.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[2,2],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":"dot-R","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[1.5,3],[0,1.5],[1.5,0],[3,1.5],[1.5,3]],"i":[[0,0],[0,0.8250000178813934],[-0.8250000178813934,0],[0,-0.8250000178813934],[0.8250000178813934,0]],"o":[[-0.8250000178813934,0],[0,-0.8250000178813934],[0.8250000178813934,0],[0,0.8250000178813934],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[3.5,-14],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[62.5,-11],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[62.5,-11],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[1.5,1.5],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":27,"ty":4,"nm":"spark-L","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[7.371442522321428,3.199999128069196],[8,4],[7.371442522321428,4.800000871930803],[5.828578404017857,5.199999128069196],[5.199986049107142,5.828569684709821],[4.800013950892857,7.371429443359375],[4,8],[3.199986049107143,7.371429443359375],[2.800013950892857,5.828569684709821],[2.171421595982143,5.199999128069196],[0.6285574776785714,4.800000871930803],[0,4],[0.6285574776785714,3.199999128069196],[2.171421595982143,2.800000871930803],[2.800013950892857,2.171430315290178],[3.199986049107143,0.628570556640625],[4,0],[4.800013950892857,0.628570556640625],[5.199986049107142,2.171430315290178],[5.828578404017857,2.800000871930803],[7.371442522321428,3.199999128069196]],"i":[[0,0],[0,-0.3999999931880406],[0.3999999931880406,-0.1142857159887041],[0,0],[0.05714285799435206,-0.2857142857142857],[0,0],[0.3999999931880406,0],[0.1142857159887041,0.3999999931880406],[0,0],[0.2857142857142857,0.05714285799435206],[0,0],[0,0.3999999931880406],[-0.3999999931880406,0.1142857159887041],[0,0],[-0.05714285799435206,0.2857142857142857],[0,0],[-0.3999999931880406,0],[-0.1142857159887041,-0.3999999931880406],[0,0],[-0.2857142857142857,-0.05714285799435206],[0,0]],"o":[[0.3428571564810616,0.05714285799435206],[0,0.3428571564810616],[0,0],[-0.2857142857142857,0.05714285799435206],[0,0],[-0.05714285799435206,0.3428571564810616],[-0.3428571564810616,0],[0,0],[-0.05714285799435206,-0.2857142857142857],[0,0],[-0.3428571564810616,-0.05714285799435206],[0,-0.3428571564810616],[0,0],[0.2857142857142857,-0.05714285799435206],[0,0],[0.05714285799435206,-0.3428571564810616],[0.3428571564810616,0],[0,0],[0.05714285799435206,0.2857142857142857],[0,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[-1,-16.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[-57,-7.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[4,4],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":1,"k":[{"t":40,"s":[360],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":130,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":370,"s":[-180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"spark-Big","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[11.05716378348214,4.799997929164549],[12,5.999999046325684],[11.05716378348214,7.200000163486817],[8.742867606026785,7.799997452327391],[7.799979073660714,8.74285313742543],[7.200020926339286,11.05714240755333],[6,11.99999809265137],[4.799979073660714,11.05714240755333],[4.200020926339286,8.74285313742543],[3.257132393973214,7.799997452327391],[0.9428362165178571,7.200000163486817],[0,5.999999046325684],[0.9428362165178571,4.799997929164549],[3.257132393973214,4.200000640323976],[4.200020926339286,3.257144955225937],[4.799979073660714,0.9428556850980385],[6,0],[7.200020926339286,0.9428556850980385],[7.799979073660714,3.257144955225937],[8.742867606026785,4.200000640323976],[11.05716378348214,4.799997929164549]],"i":[[0,0],[0,-0.5999998944146309],[0.5999999897820608,-0.1714285467352182],[0,0],[0.0857142869915281,-0.4285713604518345],[0,0],[0.5999999897820608,0],[0.1714285739830562,0.5999998944146309],[0,0],[0.4285714285714285,0.08571427336760909],[0,0],[0,0.5999998944146309],[-0.5999999897820608,0.1714285467352182],[0,0],[-0.0857142869915281,0.4285713604518345],[0,0],[-0.5999999897820608,0],[-0.1714285739830562,-0.5999998944146309],[0,0],[-0.4285714285714285,-0.08571427336760909],[0,0]],"o":[[0.5142857347215924,0.08571427336760909],[0,0.5142856529780764],[0,0],[-0.4285714285714285,0.08571427336760909],[0,0],[-0.0857142869915281,0.5142856529780764],[-0.5142857347215924,0],[0,0],[-0.0857142869915281,-0.4285713604518345],[0,0],[-0.5142857347215924,-0.08571427336760909],[0,-0.5142856529780764],[0,0],[0.4285714285714285,-0.08571427336760909],[0,0],[0.0857142869915281,-0.5142856529780764],[0.5142857347215924,0],[0,0],[0.0857142869915281,0.4285713604518345],[0,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[-1,-15.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[-43,-28.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[-43,-28.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[6,5.999999046325684],"ix":2},"s":{"a":1,"k":[{"t":130,"s":[100,-100],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":250,"s":[50,-50],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":370,"s":[100,-100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":{"a":1,"k":[{"t":40,"s":[360],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":29,"ty":4,"nm":"spark-R","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[7.371442794799805,3.199999173482259],[8,4],[7.371442794799805,4.80000082651774],[5.828578313191731,5.199999173482259],[5.199986139933268,5.828570048014322],[4.800013860066731,7.371429443359375],[4,8],[3.199986139933268,7.371429443359375],[2.800013860066731,5.828570048014322],[2.171421686808268,5.199999173482259],[0.6285574833552042,4.80000082651774],[0,4],[0.6285574833552042,3.199999173482259],[2.171421686808268,2.800000826517741],[2.800013860066731,2.171430269877116],[3.199986139933268,0.628570556640625],[4,0],[4.800013860066731,0.628570556640625],[5.199986139933268,2.171430269877116],[5.828578313191731,2.800000826517741],[7.371442794799805,3.199999173482259]],"i":[[0,0],[0,-0.4000000158945719],[0.4000000158945719,-0.1142857174078623],[0,0],[0.05714285870393117,-0.2857142885526021],[0,0],[0.4000000158945719,0],[0.1142857174078623,0.4000000158945719],[0,0],[0.2857142885526021,0.05714285870393117],[0,0],[0,0.4000000158945719],[-0.4000000158945719,0.1142857174078623],[0,0],[-0.05714285870393117,0.2857142885526021],[0,0],[-0.4000000158945719,0],[-0.1142857174078623,-0.4000000158945719],[0,0],[-0.2857142885526021,-0.05714285870393117],[0,0]],"o":[[0.3428571621576945,0.05714285870393117],[0,0.3428571621576945],[0,0],[-0.2857142885526021,0.05714285870393117],[0,0],[-0.05714285870393117,0.3428571621576945],[-0.3428571621576945,0],[0,0],[-0.05714285870393117,-0.2857142885526021],[0,0],[-0.3428571621576945,-0.05714285870393117],[0,-0.3428571621576945],[0,0],[0.2857142885526021,-0.05714285870393117],[0,0],[0.05714285870393117,-0.3428571621576945],[0.3428571621576945,0],[0,0],[0.05714285870393117,0.2857142885526021],[0,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[2,-14.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[45,-22.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[45,-22.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[4,4],"ix":2},"s":{"a":1,"k":[{"t":130,"s":[100,-100],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":250,"s":[250,-250],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":370,"s":[100,-100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":{"a":1,"k":[{"t":40,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionAsset.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionAsset.swift index dfa7a23850..a811f9bab1 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionAsset.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionAsset.swift @@ -21,8 +21,8 @@ import Foundation public enum NetworkProtectionAsset: String, CaseIterable { case ipAddressIcon = "IP-16" case serverLocationIcon = "Server-Location-16" - case vpnDisabledImage = "VPN-Disabled-128" - case vpnEnabledImage = "VPN-128" + case vpnDisabledImage = "VPNDisabled" + case vpnEnabledImage = "VPN" case vpnIcon = "VPN-16" // Apple Icons diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPN-128.imageset/Network-Protetion-VPN-128.pdf b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPN-128.imageset/Network-Protetion-VPN-128.pdf deleted file mode 100644 index 54953a618cb9ffd53f43d172d78d4bf9cc0bd323..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14844 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 WdG_i}kxuR!y1O6!=+j^Q{J#K{cyrhQ diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPN-Disabled-128.imageset/Network-Protetion-VPN-Disabled-128.pdf b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPN-Disabled-128.imageset/Network-Protetion-VPN-Disabled-128.pdf deleted file mode 100644 index bee84f6a0acf36ee399645f3fc3cf4fb7aec6efd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14855 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 diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPN-128.imageset/Contents.json b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPN.imageset/Contents.json similarity index 70% rename from LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPN-128.imageset/Contents.json rename to LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPN.imageset/Contents.json index 86f2574f00..7d29a35569 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPN-128.imageset/Contents.json +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPN.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Network-Protetion-VPN-128.pdf", + "filename" : "VPN.pdf", "idiom" : "universal" } ], diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPN.imageset/VPN.pdf b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPN.imageset/VPN.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e4423e08d1a36b0f6bd5b4d20c362b2853727768 GIT binary patch literal 14915 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 J&wl;${{-tmf<6EM literal 0 HcmV?d00001 diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNDownload.imageset/Contents.json b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNDownload.imageset/Contents.json new file mode 100644 index 0000000000..c9df096cfc --- /dev/null +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNDownload.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "vpn-download.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNDownload.imageset/vpn-download.pdf b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNDownload.imageset/vpn-download.pdf new file mode 100644 index 0000000000000000000000000000000000000000..55844a6647538a3ec000cd7c8e36e8f014467691 GIT binary patch literal 1357 zcmZXUOK;Oa5XbNPDdtkCJ#@XZ@0F@bG<^sGqNLm^4#wH0L5U5vL4{Axtlw!@QL_2P z^PhK4?yqmpkvql^1XSODFo25-$ZlTRyN5P!WB(1ik0J-btw;whFZ&mZ$@vhJuJT!4g1Bi8-#7ckasQwoQv@`U8dh8S9DMT zk)S*x6bCjv_V&^gB?}y-_ZYx%6VRZ51M}P)M5l;(?-l8Iu0;rFJEBBKP&p|Vb_FwB zS>u!g$*uG*1lZ3E1rI?cizP%7HdY}C3!{{GfK+)EjFOrAl8 zbWoeoMNH*8#JRGm71m2SdT6}rl?(1 z%Nc3zB|$%1YHk!7;mA^Rbi$J%$p+LxQjIj%S{W0@j*}W`_~4vgAj8p0ZD~+P%VKWn zs5Xay9Q{0sQ&g{5S^XTE-Q+`e)5m=&aCvW{vnMCf7`hMzG8~U=KA+PRH5! zO%LB#MiX3gG+T>t+3QI&s{}_GRM09AX3-~`46OwxxN;_%OkH5Zo-&Kp2F{E*vgvG+ zvWXLMkimEt6U_uG+0h8KJzQlNlyf!!8rDfW0@5bNl-I$v(VmhFk(2cx9K8)1?vgax zrewi%j2=?5I;Bzqfns)HM_96lQ{UP3tGu+_u@!vvEXZV-eIn@gF-8p+y``7hb>2sU zkOsSvt&mw~ATsSNAzFK56o`#MN1Pw2v}IHx$z-iI2D)vJ2NKZ;(fv zCLf~7(Yj!b(!|)@qeM2RHf$*hgHSScgxn$;hkCS+&A~$`*sEgC z#FKE+qD}@i9@DoLV;XjFC2&T9v3a9$WtO+C1{4_=au6dLQxEAQbkZA_w9d+%OspL% zVvnrYfTFJcr)LG3k8eaJK8xI5eeVp7mV0X3sFB0ie#qfo`@XHJx^3v;E7}=j^5yrB zk|uY>qU^!Pa=t7s>KA&*W$VUnqV0fov%%A}Z0hIDtZXP>7}MU}WnHypg&p%PFu=QF z14gb#xCMp~GTPlW@0W8(r5wSOhJ7yUXV~&9{SC;TC!D4|PdUJ5+;A88WziO^dU0s_ zvMC>hq9C^6=YR6f8ArhVK&`qNRU-<}na2*L+~@xw9Kb`wY>}ITRjkq818O(La#e0b zzI@+ZAsxp}y)IAacsW1WCS@|M>y~oBU4kd~%ReR5cb99iX}fS(2Pck>u0P%U1+f@H A?EnA( literal 0 HcmV?d00001 diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNUpload.imageset/Contents.json b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNUpload.imageset/Contents.json new file mode 100644 index 0000000000..4efc61b832 --- /dev/null +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNUpload.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "vpn-upload.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNUpload.imageset/vpn-upload.pdf b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNUpload.imageset/vpn-upload.pdf new file mode 100644 index 0000000000000000000000000000000000000000..97a505ff336e46e603a8833b019ac9df300c5264 GIT binary patch literal 1361 zcmZXUOHbQC5XbNPDdvI%2i80LUI`(U1W?shrJ*;}gX=6IRbsccsc1hvvv&L-tfFN7 z^UmYHGn13M<;^*A#~6Zu>gO*8aB%_I^-Ftu-{x)XpRoHWauD2#wBYix|FBrDlNpn5 z>_5kH(>{SBxT1rsY3rEpS++x7l{HQ|klae|LV(@GQ1B3BvY108$Hpoo$HFM(9gqyCf>9E5r^n7w!8DiTSkIy2 z!8#L2_vmdxW7L9;aF!l&3>Jl+avaBvx?koD^~(R9h~EHfRPrgo35&SdT6p zQ&cg!%L!@jB|$$~YHk!7;mA^Rbi$J%xecg;q&w1FYh_Frica3hhY!x_88RHL)Rvlc zuq>vMjuCdmw4h)n`RMZ)NuR6;@V*ra_QW1p$-c4F)`y-Q8a(xKKOiJ}Y64%@Os(OJ3QgG<>E!>U^51s zgfH?o-&LEprZ07TWC9%P<)?43#wY`X4~vT5?VubHwZRecG?vFmQI*mf4N(Zn^$7XN zeOu?-U94HE-p3_f$7xgT;~AW8*Ju5uWUH!bAqnm-IJ@8ekCc7)r some View { VStack(spacing: 0) { - HStack { - Spacer() - Image(model.mainImageAsset) - Spacer() + if colorScheme == .light { + headerAnimationView("vpn-light-mode") + } else { + headerAnimationView("vpn-dark-mode") } Text(model.featureStatusDescription) @@ -184,6 +185,12 @@ public struct TunnelControllerView: View { } } + @ViewBuilder + private func headerAnimationView(_ animationName: String) -> some View { + LottieView(animation: .named(animationName)) + .playing(loopMode: .loop) + } + @ViewBuilder private func statusBadge(isConnected: Bool) -> some View { Circle() From 81fcbd47a0d6d2a95dfbc09a78c8aaf5eafc9ed8 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Tue, 2 Apr 2024 22:14:59 -0400 Subject: [PATCH 04/22] Add Lottie to VPN target --- DuckDuckGo.xcodeproj/project.pbxproj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index fc174b22a8..640cc294eb 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3213,6 +3213,7 @@ BD384ACC2BBC821B00EF3735 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; BD384ACD2BBC821D00EF3735 /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; BD384ACE2BBC821D00EF3735 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; + BDE981D82BBCE4C700645880 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = BDE981D72BBCE4C700645880 /* Lottie */; }; C13909EF2B85FD4E001626ED /* AutofillActionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */; }; C13909F02B85FD4E001626ED /* AutofillActionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */; }; C13909F12B85FD4E001626ED /* AutofillActionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */; }; @@ -4861,6 +4862,7 @@ 7BEEA5122AD1235B00A9E72B /* NetworkProtectionIPC in Frameworks */, 7BA7CC5F2AD1210C0042E5CE /* Networking in Frameworks */, 7BEEA5162AD1236E00A9E72B /* NetworkProtectionUI in Frameworks */, + BDE981D82BBCE4C700645880 /* Lottie in Frameworks */, 7BFCB74E2ADE7E1A00DA3EA7 /* PixelKit in Frameworks */, EE7295ED2A545C0A008C0991 /* NetworkProtection in Frameworks */, EE2F9C5B2B90F2FF00D45FC9 /* Subscription in Frameworks */, @@ -9014,6 +9016,7 @@ 4B41EDAA2B1544B2001EEDF4 /* LoginItems */, 7B00997C2B6508B700FE7C31 /* NetworkProtectionProxy */, EE2F9C5A2B90F2FF00D45FC9 /* Subscription */, + BDE981D72BBCE4C700645880 /* Lottie */, ); productName = DuckDuckGoAgent; productReference = 4B2D06392A11CFBB00DE1F49 /* DuckDuckGo VPN.app */; @@ -15104,6 +15107,11 @@ package = B6F997B92B8F352500476735 /* XCRemoteSwiftPackageReference "apple-toolbox" */; productName = "plugin:SwiftLintPlugin"; }; + BDE981D72BBCE4C700645880 /* Lottie */ = { + isa = XCSwiftPackageProductDependency; + package = 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */; + productName = Lottie; + }; CBC83E3529B63D380008E19C /* Configuration */ = { isa = XCSwiftPackageProductDependency; package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; From cbbd23dbe87cd015aaa42579391084afc4e93170 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 3 Apr 2024 00:02:56 -0400 Subject: [PATCH 05/22] Play lottie animation with intro --- .../Extensions/LottieView+withIntro.swift | 51 +++++++++++++++++++ .../NetworkProtectionStatusView.swift | 2 +- .../TunnelControllerView.swift | 15 +++++- .../TunnelControllerViewModel.swift | 26 ++++------ 4 files changed, 74 insertions(+), 20 deletions(-) create mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/LottieView+withIntro.swift diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/LottieView+withIntro.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/LottieView+withIntro.swift new file mode 100644 index 0000000000..3bfe31d06d --- /dev/null +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/LottieView+withIntro.swift @@ -0,0 +1,51 @@ +// +// Lottie+withIntro.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// 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 SwiftUI +import Lottie + +extension LottieView where Placeholder: View { + public struct LoopWithIntroTiming { + let skipIntro: Bool + let introStartFrame: AnimationFrameTime + let introEndFrame: AnimationFrameTime + let loopStartFrame: AnimationFrameTime + let loopEndFrame: AnimationFrameTime + } + + public func playing(withIntro timing: LoopWithIntroTiming, isAnimating: Binding = .constant(true)) -> Lottie.LottieView { + configure { uiView in + if uiView.isAnimationPlaying, !isAnimating.wrappedValue { + uiView.stop() + return + } + + guard isAnimating.wrappedValue, !uiView.isAnimationPlaying else { return } + + if uiView.loopMode == .playOnce, uiView.currentProgress == 1 { return } + + if timing.skipIntro { + uiView.play(fromFrame: timing.loopStartFrame, toFrame: timing.loopEndFrame, loopMode: .loop) + } else { + uiView.play(fromFrame: timing.introStartFrame, toFrame: timing.introEndFrame, loopMode: .playOnce) { _ in + uiView.play(fromFrame: timing.loopStartFrame, toFrame: timing.loopEndFrame, loopMode: .loop) + } + } + } + } +} diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/StatusView/NetworkProtectionStatusView.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/StatusView/NetworkProtectionStatusView.swift index aae033b17d..72d8ce6391 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/StatusView/NetworkProtectionStatusView.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/StatusView/NetworkProtectionStatusView.swift @@ -82,7 +82,7 @@ public struct NetworkProtectionStatusView: View { bottomMenuView() } .padding(5) - .frame(maxWidth: 350, alignment: .top) + .frame(minWidth: 350, maxWidth: 350, alignment: .top) .transition(.slide) } diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift index 294fff02c1..14005e15a4 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift @@ -188,8 +188,19 @@ public struct TunnelControllerView: View { @ViewBuilder private func headerAnimationView(_ animationName: String) -> some View { LottieView(animation: .named(animationName)) - .playing(loopMode: .loop) - } + .configure { animationView in + animationView.contentMode = .scaleAspectFit + animationView.clipsToBounds = false + } + .playing(withIntro: .init( + // Skip the intro if NetP is enabled, but the user didn't manually trigger it + skipIntro: model.isVPNEnabled && !model.isToggleDisabled, + introStartFrame: 0, + introEndFrame: 100, + loopStartFrame: 130, + loopEndFrame: 370 + ), isAnimating: $model.isVPNEnabled) +} @ViewBuilder private func statusBadge(isConnected: Bool) -> some View { diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift index f2dae8b810..23b3e48fd9 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift @@ -28,6 +28,9 @@ public final class TunnelControllerViewModel: ObservableObject { /// private let tunnelController: TunnelController + @Published + public var isVPNEnabled = false + /// The type of extension that's being used for NetP /// @Published @@ -62,23 +65,6 @@ public final class TunnelControllerViewModel: ObservableObject { private static let connectivityIssuesDispatchQueue = DispatchQueue(label: "com.duckduckgo.NetworkProtectionStatusView.connectivityIssuesDispatchQueue", qos: .userInteractive) private static let serverInfoDispatchQueue = DispatchQueue(label: "com.duckduckgo.NetworkProtectionStatusView.serverInfoDispatchQueue", qos: .userInteractive) - // MARK: - Feature Image - - var mainImageAsset: NetworkProtectionAsset { - switch connectionStatus { - case .connected: - return .vpnEnabledImage - case .disconnecting: - if case .connected = previousConnectionStatus { - return .vpnEnabledImage - } else { - return .vpnDisabledImage - } - default: - return .vpnDisabledImage - } - } - // MARK: - Initialization & Deinitialization public init(controller: TunnelController, @@ -130,6 +116,12 @@ public final class TunnelControllerViewModel: ObservableObject { Task { @MainActor in self.connectionStatus = status + switch status { + case .connected, .connecting: + self.isVPNEnabled = true + default: + self.isVPNEnabled = false + } } } .store(in: &cancellables) From 6257eeaa3a7b321fcff5372a7fdfdf860a9fd44b Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 3 Apr 2024 00:23:24 -0400 Subject: [PATCH 06/22] Move assets --- DuckDuckGo.xcodeproj/project.pbxproj | 16 ++++++++++++++-- .../BothAppTargets/Assets}/vpn-dark-mode.json | 0 .../BothAppTargets/Assets}/vpn-light-mode.json | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) rename DuckDuckGo/{ => NetworkProtection/AppTargets/BothAppTargets/Assets}/vpn-dark-mode.json (100%) rename DuckDuckGo/{ => NetworkProtection/AppTargets/BothAppTargets/Assets}/vpn-light-mode.json (99%) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 640cc294eb..68e7539bcd 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3214,6 +3214,8 @@ BD384ACD2BBC821D00EF3735 /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; BD384ACE2BBC821D00EF3735 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; BDE981D82BBCE4C700645880 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = BDE981D72BBCE4C700645880 /* Lottie */; }; + BDE981D92BBD10D600645880 /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; + BDE981DA2BBD10D600645880 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; C13909EF2B85FD4E001626ED /* AutofillActionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */; }; C13909F02B85FD4E001626ED /* AutofillActionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */; }; C13909F12B85FD4E001626ED /* AutofillActionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */; }; @@ -5819,6 +5821,7 @@ 4B4D60632A0B29FA00BCD287 /* BothAppTargets */ = { isa = PBXGroup; children = ( + BDE981DB2BBD110800645880 /* Assets */, EEA3EEAF2B24EB5100E8333A /* VPNLocation */, 9D9AE8682AA76CDC0026E7DC /* LoginItem+NetworkProtection.swift */, 7BE146062A6A83C700C313B8 /* NetworkProtectionDebugMenu.swift */, @@ -7175,8 +7178,6 @@ children = ( AA4D700525545EDE00C3411E /* Application */, AA585D85248FD31400E9A3E2 /* Assets.xcassets */, - BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */, - BD384AC72BBC821100EF3735 /* vpn-light-mode.json */, B31055BB27A1BA0E001AC618 /* Autoconsent */, 7B1E819A27C8874900FF0E60 /* Autofill */, AAC5E4C025D6A6A9007F5990 /* Bookmarks */, @@ -8680,6 +8681,15 @@ path = View; sourceTree = ""; }; + BDE981DB2BBD110800645880 /* Assets */ = { + isa = PBXGroup; + children = ( + BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */, + BD384AC72BBC821100EF3735 /* vpn-light-mode.json */, + ); + path = Assets; + sourceTree = ""; + }; C13909F22B85FD60001626ED /* Autofill */ = { isa = PBXGroup; children = ( @@ -9630,7 +9640,9 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + BDE981DA2BBD10D600645880 /* vpn-light-mode.json in Resources */, 7BA7CC482AD11E5C0042E5CE /* Assets.xcassets in Resources */, + BDE981D92BBD10D600645880 /* vpn-dark-mode.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/DuckDuckGo/vpn-dark-mode.json b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/Assets/vpn-dark-mode.json similarity index 100% rename from DuckDuckGo/vpn-dark-mode.json rename to DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/Assets/vpn-dark-mode.json diff --git a/DuckDuckGo/vpn-light-mode.json b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/Assets/vpn-light-mode.json similarity index 99% rename from DuckDuckGo/vpn-light-mode.json rename to DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/Assets/vpn-light-mode.json index bd6adbe2e8..fc062b9223 100644 --- a/DuckDuckGo/vpn-light-mode.json +++ b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/Assets/vpn-light-mode.json @@ -1 +1 @@ -{"v":"5.7.5","fr":100,"ip":0,"op":370,"w":128,"h":96,"nm":"Comp1","ddd":0,"assets":[{"id":"0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"shadow","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[42.2357292175293,8.99384593963623],[41.2205810546875,0],[53.23579788208008,24.99367523193359],[21.23579597473145,56.99367141723633],[0,48.93241500854492],[2.235732555389404,48.99384307861328],[42.2357292175293,8.99384593963623]],"i":[[0,0],[0.664215087890625,2.89088249206543],[0,-10.11254501342773],[17.6731128692627,0],[5.649600028991699,5.015373229980469],[-0.750247597694397,0],[0,22.09139251708984]],"o":[[0,-3.092463970184326],[7.324420928955078,5.864182472229004],[0,17.6731128692627],[-8.148801803588867,0],[0.7400614619255066,0.040771484375],[22.09139251708984,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.1686274509803922,0.3333333333333333,0.792156862745098],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[69.3820571899414,35.50293731689453],"ix":2},"a":{"a":0,"k":[26.61789894104004,28.49683570861816],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":1,"k":[{"t":40,"s":[40],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[50],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"dot-nw","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"el","d":1,"s":{"a":0,"k":[7,7],"ix":2},"p":{"a":0,"k":[0,0],"ix":2}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":10,"s":[46.5,13.5],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[50.5,6.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-14.74848774275709,-23.38554600445718],"to":[-12.74848774275709,16.61445399554282]},{"t":80,"s":[53.5,66.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":81,"s":[57.5,-2.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":200,"s":[57.5,-2.5],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-20.75,-24.25],"to":[-20.75,23.08333333333333]},{"t":370,"s":[56.5,68.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"dot-ne","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"el","d":1,"s":{"a":0,"k":[7,7],"ix":2},"p":{"a":0,"k":[0,0],"ix":2}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":10,"s":[69.5,21.5],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[63.5,17.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-6.951289398280807,-10.83253740846864],"to":[9.048710601719193,4.500795924864676]},{"t":80,"s":[87.5,40.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[26.75000000000003,-1.75],"to":[-10.58333333333334,-19.75]},{"t":81,"s":[31.5,13.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":150,"s":[31.5,13.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-9.750000000000028,-19.41666666666666],"to":[27.58333333333334,-1.416666666666664]},{"t":300,"s":[87.5,40.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"dot-s","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"el","d":1,"s":{"a":0,"k":[7,7],"ix":2},"p":{"a":0,"k":[0,0],"ix":2}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":10,"s":[59.5,49.5],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[69.5,50.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[10.97134670487107,11.24832855778413],"to":[-17.69531996179562,-0.08500477554918007]},{"t":80,"s":[26.5,31.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-20.749999999999986,-1.75],"to":[10.58333333333333,11.58333333333333]},{"t":81,"s":[73.5,52.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":130,"s":[73.5,50.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[12.58333333333336,13.250000000000014],"to":[-18.75,0.5833333333333144]},{"t":230,"s":[26.5,32.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[10056,10032.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[17,30.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[-34,-13.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[9,-30.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[-13,28.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[34,0.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"globe","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[32,63.99999999999999],[0,32],[32,0],[63.99999999999999,32],[32,63.99999999999999]],"i":[[0,0],[0,17.67311330810018],[-17.67311330810018,0],[0,-17.67311330810018],[17.67311330810018,0]],"o":[[-17.67311330810018,0],[0,-17.67311330810018],[17.67311330810018,0],[0,17.67311330810018],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.8,0.8,0.8],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.3333333333333333,0.4980392156862745,0.9529411764705882],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[64,32],"ix":2},"a":{"a":0,"k":[32,32],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0}]},{"id":"1","layers":[{"ddd":0,"ind":12,"ty":4,"nm":"_mask","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"td":1,"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[55,35],[35,55],[35,61.1272168529097],[32.27027277742516,63.99888239316815],[32,64],[0,32],[32,0],[64,32],[63.99888239502384,32.27027632020794],[61.12722036453148,35],[55,35]],"i":[[0,0],[1.3527074874795e-15,-11.04569435119629],[0,-2.603342616466307],[1.549197989173602,-0.01281636670575817],[0.09017852706661245,0],[-2.164332234077084e-15,17.6731128692627],[-17.6731128692627,-2.164332234077084e-15],[2.164332445835321e-15,-17.67311477661133],[0.0007445845155729103,-0.09000398946863442],[1.549249531796702,0],[0,0]],"o":[[-11.04569244384766,-1.352707275721263e-15],[0,0],[0,1.549251002554159],[-0.09000283819779753,0.0007445848671139288],[-17.6731128692627,-2.164332234077084e-15],[2.164332022318847e-15,-17.67311096191406],[17.67311096191406,2.164332022318847e-15],[0,0.09017973898513532],[-0.01281618456122568,1.549196519872671],[-2.603342959208661,0],[0,0]]}}},{"ty":"fl","c":{"a":0,"k":[0,0,0],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[64,32],"ix":2},"a":{"a":0,"k":[32,32],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"refId":"0","w":20000,"h":20000,"ind":13,"ty":0,"nm":"globe","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"tt":1}]}],"layers":[{"ddd":0,"ind":14,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[64,48],"ix":2},"a":{"a":0,"k":[64,35],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":15,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":1,"k":[{"t":10,"s":[87,54],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":30,"s":[87,48],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":40,"s":[87,58],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":50,"s":[87,54],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[87,54],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":14},{"ddd":0,"ind":16,"ty":4,"nm":"lock-hook","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":15,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":false,"v":[[0,9],[0,4.048697948455811],[1.171572897169325,1.185836362838745],[4,2.333394250525001e-14],[6.828427208794487,1.185835719108582],[8,4.048697519302368],[8,8.46090259552002]],"i":[[0,0],[0,0],[-0.7501454883151584,0.7592780649662018],[-1.060865932040744,1.151999967419215e-7],[-0.7501456472608777,-0.7592779576778412],[-3.203726414034867e-7,-1.073781502246857],[0,0]],"o":[[0,0],[9.274384638047195e-8,-1.073781502246857],[0.7501454883151584,-0.7592780649662018],[1.060865932040744,-1.151999967419215e-7],[0.7501456472608777,0.7592779576778412],[0,0],[0,0]]}}},{"ty":"st","c":{"a":0,"k":[1,1,1],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2.666666746139526,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":1,"k":[{"t":30,"s":[85],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[87,50.5],"ix":2},"a":{"a":0,"k":[4,4.5],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":1,"k":[{"t":30,"s":[196],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":-180,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"lock-base","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":15,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[2.000000000000001,0],[14,0],[16,2.000000000000002],[16,7.333333015441899],[14,9.333333015441895],[1.999999999999996,9.333333015441895],[0,7.333333015441892],[0,1.999999999999995],[2.000000000000001,0]],"i":[[0,0],[-3.074004826695607,0],[0,-1.104569499661587],[0,-1.574570024376729],[1.104569499661587,0],[3.074004826695605,0],[0,1.104569499661587],[0,1.574570024376728],[-1.104569499661587,0]],"o":[[3.074004826695607,0],[1.104569499661587,0],[0,1.57457002437673],[0,1.104569499661587],[-3.074004826695608,0],[-1.104569499661587,0],[0,-1.57457002437673],[0,-1.104569499661587],[0,0]]}}},{"ty":"fl","c":{"a":0,"k":[1,1,1],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[87,58.66666793823242],"ix":2},"a":{"a":0,"k":[8,4.666666507720947],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":180,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":-180,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"bg","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":15,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[32,16],[16,32],[0,16],[16,0],[32,16]],"i":[[0,0],[8.836555727066532,0],[0,8.836555727066532],[-8.836555727066532,0],[0,-8.836555727066532]],"o":[[0,8.836555727066532],[-8.836555727066532,0],[0,-8.836555727066532],[8.836555727066532,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":30,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[0.2980392156862745,0.7294117647058823,0.2352941176470588],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[87,54],"ix":2},"a":{"a":0,"k":[16,16],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"refId":"1","w":20000,"h":20000,"ind":13,"ty":0,"nm":"globe","sr":1,"ks":{"p":{"a":0,"k":[64,32],"ix":2},"a":{"a":0,"k":[10064,10032],"ix":2},"s":{"a":1,"k":[{"t":40,"s":[100,100],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":60,"s":[85,85],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":70,"s":[105,105],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":80,"s":[100,100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":14},{"ddd":0,"ind":19,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[64,38.42728805541992],"ix":2},"a":{"a":0,"k":[64,38.42728805541992],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":14},{"ddd":0,"ind":20,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[108.5400009155273,55],"ix":2},"a":{"a":0,"k":[108.5400009155273,55.00000190734863],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":19},{"ddd":0,"ind":21,"ty":4,"nm":"bling-N","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":20,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":false,"v":[[0,0],[3.329999923706055,0]],"i":[[0,0],[0,0]],"o":[[0,0],[0,0]]}}},{"ty":"st","c":{"a":0,"k":[0.8,0.8,0.8],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2.5,"ix":2},"lc":2,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":1,"k":[{"t":40,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":47,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":174,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":176,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":183,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[107.4273300170898,45.32266616821289],"ix":2},"a":{"a":0,"k":[1.664999961853027,0],"ix":2},"s":{"a":0,"k":[99.9999982885729,99.99999828857288],"ix":2},"r":{"a":0,"k":-45,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"bling","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":20,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":false,"v":[[0,0],[3.329999923706055,0]],"i":[[0,0],[0,0]],"o":[[0,0],[0,0]]}}},{"ty":"st","c":{"a":0,"k":[0.8,0.8,0.8],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2.5,"ix":2},"lc":2,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":1,"k":[{"t":47,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":53,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":175,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":176,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":183,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":189,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[109.1650009155273,55],"ix":2},"a":{"a":0,"k":[1.664999961853027,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":"bling-S","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":20,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":false,"v":[[0,0],[3.329999923706055,0]],"i":[[0,0],[0,0]],"o":[[0,0],[0,0]]}}},{"ty":"st","c":{"a":0,"k":[0.8,0.8,0.8],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2.5,"ix":2},"lc":2,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":1,"k":[{"t":53,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":60,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":175,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":176,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":189,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":190,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":196,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[107.4273300170898,64.67733001708984],"ix":2},"a":{"a":0,"k":[1.664999961853027,0],"ix":2},"s":{"a":0,"k":[99.9999982885729,99.99999828857288],"ix":2},"r":{"a":0,"k":45,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":24,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[64,45.49990844726562],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":19},{"ddd":0,"ind":25,"ty":4,"nm":"dot-L","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[2,4],[0,2],[2,0],[4,2],[2,4]],"i":[[0,0],[0,1.100000023841858],[-1.100000023841858,0],[0,-1.100000023841858],[1.100000023841858,0]],"o":[[-1.100000023841858,0],[0,-1.100000023841858],[1.100000023841858,0],[0,1.100000023841858],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[-1,-15.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[-41,6.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[2,2],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":"dot-R","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[1.5,3],[0,1.5],[1.5,0],[3,1.5],[1.5,3]],"i":[[0,0],[0,0.8250000178813934],[-0.8250000178813934,0],[0,-0.8250000178813934],[0.8250000178813934,0]],"o":[[-0.8250000178813934,0],[0,-0.8250000178813934],[0.8250000178813934,0],[0,0.8250000178813934],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[3.5,-14],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[62.5,-11],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[62.5,-11],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[1.5,1.5],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":27,"ty":4,"nm":"spark-L","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[7.371442522321428,3.199999128069196],[8,4],[7.371442522321428,4.800000871930803],[5.828578404017857,5.199999128069196],[5.199986049107142,5.828569684709821],[4.800013950892857,7.371429443359375],[4,8],[3.199986049107143,7.371429443359375],[2.800013950892857,5.828569684709821],[2.171421595982143,5.199999128069196],[0.6285574776785714,4.800000871930803],[0,4],[0.6285574776785714,3.199999128069196],[2.171421595982143,2.800000871930803],[2.800013950892857,2.171430315290178],[3.199986049107143,0.628570556640625],[4,0],[4.800013950892857,0.628570556640625],[5.199986049107142,2.171430315290178],[5.828578404017857,2.800000871930803],[7.371442522321428,3.199999128069196]],"i":[[0,0],[0,-0.3999999931880406],[0.3999999931880406,-0.1142857159887041],[0,0],[0.05714285799435206,-0.2857142857142857],[0,0],[0.3999999931880406,0],[0.1142857159887041,0.3999999931880406],[0,0],[0.2857142857142857,0.05714285799435206],[0,0],[0,0.3999999931880406],[-0.3999999931880406,0.1142857159887041],[0,0],[-0.05714285799435206,0.2857142857142857],[0,0],[-0.3999999931880406,0],[-0.1142857159887041,-0.3999999931880406],[0,0],[-0.2857142857142857,-0.05714285799435206],[0,0]],"o":[[0.3428571564810616,0.05714285799435206],[0,0.3428571564810616],[0,0],[-0.2857142857142857,0.05714285799435206],[0,0],[-0.05714285799435206,0.3428571564810616],[-0.3428571564810616,0],[0,0],[-0.05714285799435206,-0.2857142857142857],[0,0],[-0.3428571564810616,-0.05714285799435206],[0,-0.3428571564810616],[0,0],[0.2857142857142857,-0.05714285799435206],[0,0],[0.05714285799435206,-0.3428571564810616],[0.3428571564810616,0],[0,0],[0.05714285799435206,0.2857142857142857],[0,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[-1,-16.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[-57,-7.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[4,4],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":1,"k":[{"t":40,"s":[360],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":130,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":370,"s":[-180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"spark-Big","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[11.05716378348214,4.799997929164549],[12,5.999999046325684],[11.05716378348214,7.200000163486817],[8.742867606026785,7.799997452327391],[7.799979073660714,8.74285313742543],[7.200020926339286,11.05714240755333],[6,11.99999809265137],[4.799979073660714,11.05714240755333],[4.200020926339286,8.74285313742543],[3.257132393973214,7.799997452327391],[0.9428362165178571,7.200000163486817],[0,5.999999046325684],[0.9428362165178571,4.799997929164549],[3.257132393973214,4.200000640323976],[4.200020926339286,3.257144955225937],[4.799979073660714,0.9428556850980385],[6,0],[7.200020926339286,0.9428556850980385],[7.799979073660714,3.257144955225937],[8.742867606026785,4.200000640323976],[11.05716378348214,4.799997929164549]],"i":[[0,0],[0,-0.5999998944146309],[0.5999999897820608,-0.1714285467352182],[0,0],[0.0857142869915281,-0.4285713604518345],[0,0],[0.5999999897820608,0],[0.1714285739830562,0.5999998944146309],[0,0],[0.4285714285714285,0.08571427336760909],[0,0],[0,0.5999998944146309],[-0.5999999897820608,0.1714285467352182],[0,0],[-0.0857142869915281,0.4285713604518345],[0,0],[-0.5999999897820608,0],[-0.1714285739830562,-0.5999998944146309],[0,0],[-0.4285714285714285,-0.08571427336760909],[0,0]],"o":[[0.5142857347215924,0.08571427336760909],[0,0.5142856529780764],[0,0],[-0.4285714285714285,0.08571427336760909],[0,0],[-0.0857142869915281,0.5142856529780764],[-0.5142857347215924,0],[0,0],[-0.0857142869915281,-0.4285713604518345],[0,0],[-0.5142857347215924,-0.08571427336760909],[0,-0.5142856529780764],[0,0],[0.4285714285714285,-0.08571427336760909],[0,0],[0.0857142869915281,-0.5142856529780764],[0.5142857347215924,0],[0,0],[0.0857142869915281,0.4285713604518345],[0,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[-1,-15.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[-43,-28.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[-43,-28.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[6,5.999999046325684],"ix":2},"s":{"a":1,"k":[{"t":130,"s":[100,-100],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":250,"s":[50,-50],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":370,"s":[100,-100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":{"a":1,"k":[{"t":40,"s":[360],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":29,"ty":4,"nm":"spark-R","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[7.371442794799805,3.199999173482259],[8,4],[7.371442794799805,4.80000082651774],[5.828578313191731,5.199999173482259],[5.199986139933268,5.828570048014322],[4.800013860066731,7.371429443359375],[4,8],[3.199986139933268,7.371429443359375],[2.800013860066731,5.828570048014322],[2.171421686808268,5.199999173482259],[0.6285574833552042,4.80000082651774],[0,4],[0.6285574833552042,3.199999173482259],[2.171421686808268,2.800000826517741],[2.800013860066731,2.171430269877116],[3.199986139933268,0.628570556640625],[4,0],[4.800013860066731,0.628570556640625],[5.199986139933268,2.171430269877116],[5.828578313191731,2.800000826517741],[7.371442794799805,3.199999173482259]],"i":[[0,0],[0,-0.4000000158945719],[0.4000000158945719,-0.1142857174078623],[0,0],[0.05714285870393117,-0.2857142885526021],[0,0],[0.4000000158945719,0],[0.1142857174078623,0.4000000158945719],[0,0],[0.2857142885526021,0.05714285870393117],[0,0],[0,0.4000000158945719],[-0.4000000158945719,0.1142857174078623],[0,0],[-0.05714285870393117,0.2857142885526021],[0,0],[-0.4000000158945719,0],[-0.1142857174078623,-0.4000000158945719],[0,0],[-0.2857142885526021,-0.05714285870393117],[0,0]],"o":[[0.3428571621576945,0.05714285870393117],[0,0.3428571621576945],[0,0],[-0.2857142885526021,0.05714285870393117],[0,0],[-0.05714285870393117,0.3428571621576945],[-0.3428571621576945,0],[0,0],[-0.05714285870393117,-0.2857142885526021],[0,0],[-0.3428571621576945,-0.05714285870393117],[0,-0.3428571621576945],[0,0],[0.2857142885526021,-0.05714285870393117],[0,0],[0.05714285870393117,-0.3428571621576945],[0.3428571621576945,0],[0,0],[0.05714285870393117,0.2857142885526021],[0,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[2,-14.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[45,-22.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[45,-22.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[4,4],"ix":2},"s":{"a":1,"k":[{"t":130,"s":[100,-100],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":250,"s":[250,-250],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":370,"s":[100,-100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":{"a":1,"k":[{"t":40,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0}],"markers":[]} \ No newline at end of file +{"v":"5.7.5","fr":100,"ip":0,"op":370,"w":128,"h":96,"nm":"Comp1","ddd":0,"assets":[{"id":"0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"shadow","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[42.2357292175293,8.99384593963623],[41.2205810546875,0],[53.23579788208008,24.99367523193359],[21.23579597473145,56.99367141723633],[0,48.93241500854492],[2.235732555389404,48.99384307861328],[42.2357292175293,8.99384593963623]],"i":[[0,0],[0.664215087890625,2.89088249206543],[0,-10.11254501342773],[17.6731128692627,0],[5.649600028991699,5.015373229980469],[-0.750247597694397,0],[0,22.09139251708984]],"o":[[0,-3.092463970184326],[7.324420928955078,5.864182472229004],[0,17.6731128692627],[-8.148801803588867,0],[0.7400614619255066,0.040771484375],[22.09139251708984,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.1686274509803922,0.3333333333333333,0.792156862745098],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[69.3820571899414,35.50293731689453],"ix":2},"a":{"a":0,"k":[26.61789894104004,28.49683570861816],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":1,"k":[{"t":40,"s":[40],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[50],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"dot-nw","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"el","d":1,"s":{"a":0,"k":[7,7],"ix":2},"p":{"a":0,"k":[0,0],"ix":2}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":10,"s":[46.5,13.5],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[50.5,6.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-14.74848774275709,-23.38554600445718],"to":[-12.74848774275709,16.61445399554282]},{"t":80,"s":[53.5,66.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":81,"s":[57.5,-2.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":200,"s":[57.5,-2.5],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-20.75,-24.25],"to":[-20.75,23.08333333333333]},{"t":370,"s":[56.5,68.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"dot-ne","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"el","d":1,"s":{"a":0,"k":[7,7],"ix":2},"p":{"a":0,"k":[0,0],"ix":2}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":10,"s":[69.5,21.5],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[63.5,17.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-6.951289398280807,-10.83253740846864],"to":[9.048710601719193,4.500795924864676]},{"t":80,"s":[87.5,40.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[26.75000000000003,-1.75],"to":[-10.58333333333334,-19.75]},{"t":81,"s":[31.5,13.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":150,"s":[31.5,13.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-9.750000000000028,-19.41666666666666],"to":[27.58333333333334,-1.416666666666664]},{"t":300,"s":[87.5,40.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"dot-s","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"el","d":1,"s":{"a":0,"k":[7,7],"ix":2},"p":{"a":0,"k":[0,0],"ix":2}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":10,"s":[59.5,49.5],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[69.5,50.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[10.97134670487107,11.24832855778413],"to":[-17.69531996179562,-0.08500477554918007]},{"t":80,"s":[26.5,31.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[-20.749999999999986,-1.75],"to":[10.58333333333333,11.58333333333333]},{"t":81,"s":[73.5,52.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":130,"s":[73.5,50.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]},"ti":[12.58333333333336,13.250000000000014],"to":[-18.75,0.5833333333333144]},{"t":230,"s":[26.5,32.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[10056,10032.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[17,30.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[-34,-13.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[9,-30.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[-13,28.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Oval","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":5,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[48,96],[0,48],[48,0],[96,48],[48,96]],"i":[[0,0],[0,26.50966996215028],[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0]],"o":[[-26.50966996215028,0],[0,-26.50966996215028],[26.50966996215028,0],[0,26.50966996215028],[0,0]]}}},{"ty":"st","c":{"a":1,"k":[{"t":40,"s":[0.9333333333333333,0.9333333333333333,0.9333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.6784313725490196,0.7607843137254902,0.9882352941176471],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[34,0.5],"ix":2},"a":{"a":0,"k":[48,48],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"globe","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[32,63.99999999999999],[0,32],[32,0],[63.99999999999999,32],[32,63.99999999999999]],"i":[[0,0],[0,17.67311330810018],[-17.67311330810018,0],[0,-17.67311330810018],[17.67311330810018,0]],"o":[[-17.67311330810018,0],[0,-17.67311330810018],[17.67311330810018,0],[0,17.67311330810018],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":40,"s":[0.8,0.8,0.8],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":50,"s":[0.3333333333333333,0.4980392156862745,0.9529411764705882],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[64,32],"ix":2},"a":{"a":0,"k":[32,32],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0}]},{"id":"1","layers":[{"ddd":0,"ind":12,"ty":4,"nm":"_mask","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"td":1,"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[55,35],[35,55],[35,61.1272168529097],[32.27027277742516,63.99888239316815],[32,64],[0,32],[32,0],[64,32],[63.99888239502384,32.27027632020794],[61.12722036453148,35],[55,35]],"i":[[0,0],[1.3527074874795e-15,-11.04569435119629],[0,-2.603342616466307],[1.549197989173602,-0.01281636670575817],[0.09017852706661245,0],[-2.164332234077084e-15,17.6731128692627],[-17.6731128692627,-2.164332234077084e-15],[2.164332445835321e-15,-17.67311477661133],[0.0007445845155729103,-0.09000398946863442],[1.549249531796702,0],[0,0]],"o":[[-11.04569244384766,-1.352707275721263e-15],[0,0],[0,1.549251002554159],[-0.09000283819779753,0.0007445848671139288],[-17.6731128692627,-2.164332234077084e-15],[2.164332022318847e-15,-17.67311096191406],[17.67311096191406,2.164332022318847e-15],[0,0.09017973898513532],[-0.01281618456122568,1.549196519872671],[-2.603342959208661,0],[0,0]]}}},{"ty":"fl","c":{"a":0,"k":[0,0,0],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[64,32],"ix":2},"a":{"a":0,"k":[32,32],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"refId":"0","w":20000,"h":20000,"ind":13,"ty":0,"nm":"globe","sr":1,"ks":{"p":{"a":0,"k":[10000,10000],"ix":2},"a":{"a":0,"k":[10000,10000],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"tt":1}]}],"layers":[{"ddd":0,"ind":14,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[64,48],"ix":2},"a":{"a":0,"k":[64,35],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":15,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":1,"k":[{"t":10,"s":[87,54],"i":{"x":[0.58],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":30,"s":[87,48],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":40,"s":[87,58],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":50,"s":[87,54],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[87,54],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":14},{"ddd":0,"ind":16,"ty":4,"nm":"lock-hook","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":15,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":false,"v":[[0,9],[0,4.048697948455811],[1.171572897169325,1.185836362838745],[4,2.333394250525001e-14],[6.828427208794487,1.185835719108582],[8,4.048697519302368],[8,8.46090259552002]],"i":[[0,0],[0,0],[-0.7501454883151584,0.7592780649662018],[-1.060865932040744,1.151999967419215e-7],[-0.7501456472608777,-0.7592779576778412],[-3.203726414034867e-7,-1.073781502246857],[0,0]],"o":[[0,0],[9.274384638047195e-8,-1.073781502246857],[0.7501454883151584,-0.7592780649662018],[1.060865932040744,-1.151999967419215e-7],[0.7501456472608777,0.7592779576778412],[0,0],[0,0]]}}},{"ty":"st","c":{"a":0,"k":[1,1,1],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2.666666746139526,"ix":2},"lc":1,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":1,"k":[{"t":30,"s":[85],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[87,50.5],"ix":2},"a":{"a":0,"k":[4,4.5],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":1,"k":[{"t":30,"s":[196],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":-180,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"lock-base","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":15,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[2.000000000000001,0],[14,0],[16,2.000000000000002],[16,7.333333015441899],[14,9.333333015441895],[1.999999999999996,9.333333015441895],[0,7.333333015441892],[0,1.999999999999995],[2.000000000000001,0]],"i":[[0,0],[-3.074004826695607,0],[0,-1.104569499661587],[0,-1.574570024376729],[1.104569499661587,0],[3.074004826695605,0],[0,1.104569499661587],[0,1.574570024376728],[-1.104569499661587,0]],"o":[[3.074004826695607,0],[1.104569499661587,0],[0,1.57457002437673],[0,1.104569499661587],[-3.074004826695608,0],[-1.104569499661587,0],[0,-1.57457002437673],[0,-1.104569499661587],[0,0]]}}},{"ty":"fl","c":{"a":0,"k":[1,1,1],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[87,58.66666793823242],"ix":2},"a":{"a":0,"k":[8,4.666666507720947],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":0,"k":180,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":-180,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"bg","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":15,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[32,16],[16,32],[0,16],[16,0],[32,16]],"i":[[0,0],[8.836555727066532,0],[0,8.836555727066532],[-8.836555727066532,0],[0,-8.836555727066532]],"o":[[0,8.836555727066532],[-8.836555727066532,0],[0,-8.836555727066532],[8.836555727066532,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":30,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":40,"s":[0.2980392156862745,0.7294117647058823,0.2352941176470588],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[87,54],"ix":2},"a":{"a":0,"k":[16,16],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"refId":"1","w":20000,"h":20000,"ind":13,"ty":0,"nm":"globe","sr":1,"ks":{"p":{"a":0,"k":[64,32],"ix":2},"a":{"a":0,"k":[10064,10032],"ix":2},"s":{"a":1,"k":[{"t":40,"s":[100,100],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":60,"s":[85,85],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":70,"s":[105,105],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":80,"s":[100,100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":14},{"ddd":0,"ind":19,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[64,38.42728805541992],"ix":2},"a":{"a":0,"k":[64,38.42728805541992],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":14},{"ddd":0,"ind":20,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[108.5400009155273,55],"ix":2},"a":{"a":0,"k":[108.5400009155273,55.00000190734863],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":19},{"ddd":0,"ind":21,"ty":4,"nm":"bling-N","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":20,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":false,"v":[[0,0],[3.329999923706055,0]],"i":[[0,0],[0,0]],"o":[[0,0],[0,0]]}}},{"ty":"st","c":{"a":0,"k":[0.8,0.8,0.8],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2.5,"ix":2},"lc":2,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":1,"k":[{"t":40,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":47,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":174,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":176,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":183,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[107.4273300170898,45.32266616821289],"ix":2},"a":{"a":0,"k":[1.664999961853027,0],"ix":2},"s":{"a":0,"k":[99.9999982885729,99.99999828857288],"ix":2},"r":{"a":0,"k":-45,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"bling","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":20,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":false,"v":[[0,0],[3.329999923706055,0]],"i":[[0,0],[0,0]],"o":[[0,0],[0,0]]}}},{"ty":"st","c":{"a":0,"k":[0.8,0.8,0.8],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2.5,"ix":2},"lc":2,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":1,"k":[{"t":47,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":53,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":175,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":176,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":183,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":189,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[109.1650009155273,55],"ix":2},"a":{"a":0,"k":[1.664999961853027,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":"bling-S","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":20,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":false,"v":[[0,0],[3.329999923706055,0]],"i":[[0,0],[0,0]],"o":[[0,0],[0,0]]}}},{"ty":"st","c":{"a":0,"k":[0.8,0.8,0.8],"ix":2},"o":{"a":0,"k":100,"ix":2},"w":{"a":0,"k":2.5,"ix":2},"lc":2,"lj":1},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":1,"k":[{"t":53,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":60,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":175,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":176,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":189,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":190,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":196,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":0,"k":[107.4273300170898,64.67733001708984],"ix":2},"a":{"a":0,"k":[1.664999961853027,0],"ix":2},"s":{"a":0,"k":[99.9999982885729,99.99999828857288],"ix":2},"r":{"a":0,"k":45,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":24,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[64,45.49990844726562],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"ip":0,"op":371,"st":0,"bm":0,"parent":19},{"ddd":0,"ind":25,"ty":4,"nm":"dot-L","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[2,4],[0,2],[2,0],[4,2],[2,4]],"i":[[0,0],[0,1.100000023841858],[-1.100000023841858,0],[0,-1.100000023841858],[1.100000023841858,0]],"o":[[-1.100000023841858,0],[0,-1.100000023841858],[1.100000023841858,0],[0,1.100000023841858],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[-1,-15.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[-41,6.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[2,2],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":"dot-R","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[1.5,3],[0,1.5],[1.5,0],[3,1.5],[1.5,3]],"i":[[0,0],[0,0.8250000178813934],[-0.8250000178813934,0],[0,-0.8250000178813934],[0.8250000178813934,0]],"o":[[-0.8250000178813934,0],[0,-0.8250000178813934],[0.8250000178813934,0],[0,0.8250000178813934],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[3.5,-14],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[62.5,-11],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[62.5,-11],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[1.5,1.5],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":27,"ty":4,"nm":"spark-L","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[7.371442522321428,3.199999128069196],[8,4],[7.371442522321428,4.800000871930803],[5.828578404017857,5.199999128069196],[5.199986049107142,5.828569684709821],[4.800013950892857,7.371429443359375],[4,8],[3.199986049107143,7.371429443359375],[2.800013950892857,5.828569684709821],[2.171421595982143,5.199999128069196],[0.6285574776785714,4.800000871930803],[0,4],[0.6285574776785714,3.199999128069196],[2.171421595982143,2.800000871930803],[2.800013950892857,2.171430315290178],[3.199986049107143,0.628570556640625],[4,0],[4.800013950892857,0.628570556640625],[5.199986049107142,2.171430315290178],[5.828578404017857,2.800000871930803],[7.371442522321428,3.199999128069196]],"i":[[0,0],[0,-0.3999999931880406],[0.3999999931880406,-0.1142857159887041],[0,0],[0.05714285799435206,-0.2857142857142857],[0,0],[0.3999999931880406,0],[0.1142857159887041,0.3999999931880406],[0,0],[0.2857142857142857,0.05714285799435206],[0,0],[0,0.3999999931880406],[-0.3999999931880406,0.1142857159887041],[0,0],[-0.05714285799435206,0.2857142857142857],[0,0],[-0.3999999931880406,0],[-0.1142857159887041,-0.3999999931880406],[0,0],[-0.2857142857142857,-0.05714285799435206],[0,0]],"o":[[0.3428571564810616,0.05714285799435206],[0,0.3428571564810616],[0,0],[-0.2857142857142857,0.05714285799435206],[0,0],[-0.05714285799435206,0.3428571564810616],[-0.3428571564810616,0],[0,0],[-0.05714285799435206,-0.2857142857142857],[0,0],[-0.3428571564810616,-0.05714285799435206],[0,-0.3428571564810616],[0,0],[0.2857142857142857,-0.05714285799435206],[0,0],[0.05714285799435206,-0.3428571564810616],[0.3428571564810616,0],[0,0],[0.05714285799435206,0.2857142857142857],[0,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[-1,-16.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[-57,-7.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[4,4],"ix":2},"s":{"a":0,"k":[100,-100],"ix":2},"r":{"a":1,"k":[{"t":40,"s":[360],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":130,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":370,"s":[-180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"spark-Big","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[11.05716378348214,4.799997929164549],[12,5.999999046325684],[11.05716378348214,7.200000163486817],[8.742867606026785,7.799997452327391],[7.799979073660714,8.74285313742543],[7.200020926339286,11.05714240755333],[6,11.99999809265137],[4.799979073660714,11.05714240755333],[4.200020926339286,8.74285313742543],[3.257132393973214,7.799997452327391],[0.9428362165178571,7.200000163486817],[0,5.999999046325684],[0.9428362165178571,4.799997929164549],[3.257132393973214,4.200000640323976],[4.200020926339286,3.257144955225937],[4.799979073660714,0.9428556850980385],[6,0],[7.200020926339286,0.9428556850980385],[7.799979073660714,3.257144955225937],[8.742867606026785,4.200000640323976],[11.05716378348214,4.799997929164549]],"i":[[0,0],[0,-0.5999998944146309],[0.5999999897820608,-0.1714285467352182],[0,0],[0.0857142869915281,-0.4285713604518345],[0,0],[0.5999999897820608,0],[0.1714285739830562,0.5999998944146309],[0,0],[0.4285714285714285,0.08571427336760909],[0,0],[0,0.5999998944146309],[-0.5999999897820608,0.1714285467352182],[0,0],[-0.0857142869915281,0.4285713604518345],[0,0],[-0.5999999897820608,0],[-0.1714285739830562,-0.5999998944146309],[0,0],[-0.4285714285714285,-0.08571427336760909],[0,0]],"o":[[0.5142857347215924,0.08571427336760909],[0,0.5142856529780764],[0,0],[-0.4285714285714285,0.08571427336760909],[0,0],[-0.0857142869915281,0.5142856529780764],[-0.5142857347215924,0],[0,0],[-0.0857142869915281,-0.4285713604518345],[0,0],[-0.5142857347215924,-0.08571427336760909],[0,-0.5142856529780764],[0,0],[0.4285714285714285,-0.08571427336760909],[0,0],[0.0857142869915281,-0.5142856529780764],[0.5142857347215924,0],[0,0],[0.0857142869915281,0.4285713604518345],[0,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[-1,-15.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[-43,-28.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[-43,-28.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[6,5.999999046325684],"ix":2},"s":{"a":1,"k":[{"t":130,"s":[100,-100],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":250,"s":[50,-50],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":370,"s":[100,-100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":{"a":1,"k":[{"t":40,"s":[360],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0},{"ddd":0,"ind":29,"ty":4,"nm":"spark-R","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2}},"ao":0,"parent":24,"shapes":[{"ty":"gr","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[7.371442794799805,3.199999173482259],[8,4],[7.371442794799805,4.80000082651774],[5.828578313191731,5.199999173482259],[5.199986139933268,5.828570048014322],[4.800013860066731,7.371429443359375],[4,8],[3.199986139933268,7.371429443359375],[2.800013860066731,5.828570048014322],[2.171421686808268,5.199999173482259],[0.6285574833552042,4.80000082651774],[0,4],[0.6285574833552042,3.199999173482259],[2.171421686808268,2.800000826517741],[2.800013860066731,2.171430269877116],[3.199986139933268,0.628570556640625],[4,0],[4.800013860066731,0.628570556640625],[5.199986139933268,2.171430269877116],[5.828578313191731,2.800000826517741],[7.371442794799805,3.199999173482259]],"i":[[0,0],[0,-0.4000000158945719],[0.4000000158945719,-0.1142857174078623],[0,0],[0.05714285870393117,-0.2857142885526021],[0,0],[0.4000000158945719,0],[0.1142857174078623,0.4000000158945719],[0,0],[0.2857142885526021,0.05714285870393117],[0,0],[0,0.4000000158945719],[-0.4000000158945719,0.1142857174078623],[0,0],[-0.05714285870393117,0.2857142885526021],[0,0],[-0.4000000158945719,0],[-0.1142857174078623,-0.4000000158945719],[0,0],[-0.2857142885526021,-0.05714285870393117],[0,0]],"o":[[0.3428571621576945,0.05714285870393117],[0,0.3428571621576945],[0,0],[-0.2857142885526021,0.05714285870393117],[0,0],[-0.05714285870393117,0.3428571621576945],[-0.3428571621576945,0],[0,0],[-0.05714285870393117,-0.2857142885526021],[0,0],[-0.3428571621576945,-0.05714285870393117],[0,-0.3428571621576945],[0,0],[0.2857142885526021,-0.05714285870393117],[0,0],[0.05714285870393117,-0.3428571621576945],[0.3428571621576945,0],[0,0],[0.05714285870393117,0.2857142885526021],[0,0],[0,0]]}}},{"ty":"fl","c":{"a":1,"k":[{"t":75,"s":[0.9764705882352941,0.7450980392156863,0.1019607843137255],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[0.5333333333333333,0.5333333333333333,0.5333333333333333],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":1,"k":[{"t":75,"s":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[20],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":1,"bm":0},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":2},"m":1},{"ty":"tr","p":{"a":1,"k":[{"t":40,"s":[2,-14.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[45,-22.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[45,-22.5],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"a":{"a":0,"k":[4,4],"ix":2},"s":{"a":1,"k":[{"t":130,"s":[100,-100],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":250,"s":[250,-250],"i":{"x":[0.8],"y":[1]},"o":{"x":[0.4],"y":[0]}},{"t":370,"s":[100,-100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"r":{"a":1,"k":[{"t":40,"s":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":75,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":100,"s":[180],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}],"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":371,"st":0,"bm":0}],"markers":[]} From 2df0228e8d218d0d8921b0972ff5a27532f60fa3 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 3 Apr 2024 00:56:19 -0400 Subject: [PATCH 07/22] Use correct size --- .../NetworkProtectionStatusView.swift | 2 +- .../TunnelControllerView.swift | 17 +++++++++++------ .../TunnelControllerViewModel.swift | 2 ++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/StatusView/NetworkProtectionStatusView.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/StatusView/NetworkProtectionStatusView.swift index 72d8ce6391..bc3caa84c3 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/StatusView/NetworkProtectionStatusView.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/StatusView/NetworkProtectionStatusView.swift @@ -82,7 +82,7 @@ public struct NetworkProtectionStatusView: View { bottomMenuView() } .padding(5) - .frame(minWidth: 350, maxWidth: 350, alignment: .top) + .frame(width: 350, alignment: .top) .transition(.slide) } diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift index 14005e15a4..e653f60de0 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift @@ -165,11 +165,8 @@ public struct TunnelControllerView: View { /// private func headerView() -> some View { VStack(spacing: 0) { - if colorScheme == .light { - headerAnimationView("vpn-light-mode") - } else { - headerAnimationView("vpn-dark-mode") - } + headerAnimationView() + .frame(width: 100, height: 75) Text(model.featureStatusDescription) .applyTitleAttributes(colorScheme: colorScheme) @@ -185,6 +182,15 @@ public struct TunnelControllerView: View { } } + @ViewBuilder + private func headerAnimationView() -> some View { + if colorScheme == .light { + headerAnimationView("vpn-light-mode") + } else { + headerAnimationView("vpn-dark-mode") + } + } + @ViewBuilder private func headerAnimationView(_ animationName: String) -> some View { LottieView(animation: .named(animationName)) @@ -287,4 +293,3 @@ public struct TunnelControllerView: View { .padding(EdgeInsets(top: 6, leading: 10, bottom: 6, trailing: 9)) } } - diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift index 23b3e48fd9..7fd4ffc654 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift @@ -28,6 +28,8 @@ public final class TunnelControllerViewModel: ObservableObject { /// private let tunnelController: TunnelController + /// Whether the VPN is enabled + /// This is determined based on the connection status, same as the iOS version @Published public var isVPNEnabled = false From ff913e877d8851bbf1b57a339f3bd2526308b2ad Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 3 Apr 2024 12:26:01 -0400 Subject: [PATCH 08/22] Skip intro if not user initiated --- .../TunnelControllerView/TunnelControllerView.swift | 3 +-- .../TunnelControllerViewModel.swift | 11 +++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift index e653f60de0..021488c85b 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift @@ -199,8 +199,7 @@ public struct TunnelControllerView: View { animationView.clipsToBounds = false } .playing(withIntro: .init( - // Skip the intro if NetP is enabled, but the user didn't manually trigger it - skipIntro: model.isVPNEnabled && !model.isToggleDisabled, + skipIntro: model.isVPNEnabled && !model.isToggleDisabled && !model.isToggleUserInitiated, introStartFrame: 0, introEndFrame: 100, loopStartFrame: 130, diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift index 7fd4ffc654..ee7dea4518 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift @@ -33,6 +33,9 @@ public final class TunnelControllerViewModel: ObservableObject { @Published public var isVPNEnabled = false + @Published + public var isToggleUserInitiated = false + /// The type of extension that's being used for NetP /// @Published @@ -228,6 +231,14 @@ public final class TunnelControllerViewModel: ObservableObject { } self.internalIsRunning = newValue + switch self.toggleTransition { + case .switchingOff(let locallyInitiated) where locallyInitiated: + self.isToggleUserInitiated = true + case .switchingOn(let locallyInitiated) where locallyInitiated: + self.isToggleUserInitiated = true + default: + self.isToggleUserInitiated = false + } if newValue { self.startNetworkProtection() From 18a73c34b2a8ca2652dd36f83be1430ea1faab01 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 3 Apr 2024 12:36:27 -0400 Subject: [PATCH 09/22] Add Lottie to VPN App Store --- DuckDuckGo.xcodeproj/project.pbxproj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 68e7539bcd..12ee103188 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3216,6 +3216,7 @@ BDE981D82BBCE4C700645880 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = BDE981D72BBCE4C700645880 /* Lottie */; }; BDE981D92BBD10D600645880 /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; BDE981DA2BBD10D600645880 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; + BDE981DF2BBDBD0100645880 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = BDE981DE2BBDBD0100645880 /* Lottie */; }; C13909EF2B85FD4E001626ED /* AutofillActionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */; }; C13909F02B85FD4E001626ED /* AutofillActionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */; }; C13909F12B85FD4E001626ED /* AutofillActionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */; }; @@ -4878,6 +4879,7 @@ files = ( 7BFCB7502ADE7E2300DA3EA7 /* PixelKit in Frameworks */, 4BCBE45C2BA7E18500FC75A1 /* Subscription in Frameworks */, + BDE981DF2BBDBD0100645880 /* Lottie in Frameworks */, 7BA7CC612AD1211C0042E5CE /* Networking in Frameworks */, 7BEEA5142AD1236300A9E72B /* NetworkProtectionIPC in Frameworks */, 7B00997F2B6508C200FE7C31 /* NetworkProtectionProxy in Frameworks */, @@ -9060,6 +9062,7 @@ 7B00997E2B6508C200FE7C31 /* NetworkProtectionProxy */, 4BA7C4DC2B3F64E500AFE511 /* LoginItems */, 4BCBE45B2BA7E18500FC75A1 /* Subscription */, + BDE981DE2BBDBD0100645880 /* Lottie */, ); productName = DuckDuckGoAgentAppStore; productReference = 4B2D06692A13318400DE1F49 /* DuckDuckGo VPN App Store.app */; @@ -15124,6 +15127,11 @@ package = 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */; productName = Lottie; }; + BDE981DE2BBDBD0100645880 /* Lottie */ = { + isa = XCSwiftPackageProductDependency; + package = 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */; + productName = Lottie; + }; CBC83E3529B63D380008E19C /* Configuration */ = { isa = XCSwiftPackageProductDependency; package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; From 0c9b71601d4828d15e7e4acbdfdd7026dbbb867f Mon Sep 17 00:00:00 2001 From: Anh Do Date: Thu, 4 Apr 2024 18:43:21 -0400 Subject: [PATCH 10/22] Address warnings --- .../NetworkProtectionUI/Extensions/LottieView+withIntro.swift | 2 +- .../Extensions/UserText+NetworkProtectionUI.swift | 2 +- .../Views/TunnelControllerView/TunnelControllerView.swift | 2 +- .../NetworkProtectionAssetTests.swift | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/LottieView+withIntro.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/LottieView+withIntro.swift index 3bfe31d06d..bfa0e14367 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/LottieView+withIntro.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/LottieView+withIntro.swift @@ -1,5 +1,5 @@ // -// Lottie+withIntro.swift +// LottieView+withIntro.swift // // Copyright © 2024 DuckDuckGo. All rights reserved. // diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/UserText+NetworkProtectionUI.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/UserText+NetworkProtectionUI.swift index eca9eeefd8..174b133a09 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/UserText+NetworkProtectionUI.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/UserText+NetworkProtectionUI.swift @@ -20,7 +20,7 @@ import Foundation final class UserText { static let networkProtectionStatusHeaderMessageOff = NSLocalizedString("network.protection.status.header.message.off", value: "Connect to secure all of your device’s\nInternet traffic.", comment: "Message label text for the status view when VPN is disconnected") - static let networkProtectionStatusHeaderMessageOn = NSLocalizedString("network.protection.status.header.message.on", value: "All device Internet traffic is being secured\nthrough the VPN.", comment: "Message label text for the status view when VPN is disconnected") + static let networkProtectionStatusHeaderMessageOn = NSLocalizedString("network.protection.status.header.message.on", value: "All device Internet traffic is being secured\nthrough the VPN.", comment: "Message label text for the status view when VPN is connected") static let networkProtectionStatusViewConnDetails = NSLocalizedString("network.protection.status.view.connection.details", value: "Connection Details", comment: "Connection details label shown in NetworkProtection's status view.") static let networkProtectionStatusViewConnLabel = NSLocalizedString("network.protection.status.view.connection.label", value: "VPN", comment: "Connection label shown in NetworkProtection's status view.") static let networkProtectionStatusViewLocation = NSLocalizedString("network.protection.status.view.location", value: "Location", comment: "Location label shown in NetworkProtection's status view.") diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift index 021488c85b..219252d85a 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift @@ -196,7 +196,7 @@ public struct TunnelControllerView: View { LottieView(animation: .named(animationName)) .configure { animationView in animationView.contentMode = .scaleAspectFit - animationView.clipsToBounds = false + animationView.clipsToBounds = true } .playing(withIntro: .init( skipIntro: model.isVPNEnabled && !model.isToggleDisabled && !model.isToggleUserInitiated, diff --git a/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionAssetTests.swift b/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionAssetTests.swift index 31f40a4ab6..50e900dbca 100644 --- a/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionAssetTests.swift +++ b/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionAssetTests.swift @@ -30,8 +30,8 @@ final class NetworkProtectionAssetTests: XCTestCase { let assetsAndExpectedRawValues: [NetworkProtectionAsset: String] = [ .ipAddressIcon: "IP-16", .serverLocationIcon: "Server-Location-16", - .vpnDisabledImage: "VPN-Disabled-128", - .vpnEnabledImage: "VPN-128", + .vpnDisabledImage: "VPN-Disabled", + .vpnEnabledImage: "VPN", .vpnIcon: "VPN-16", .appleVaultIcon: "apple-vault-icon", .appleVPNIcon: "apple-vpn-icon", From ea471c552c9c6ee9c65393a62de98eb057b4ceae Mon Sep 17 00:00:00 2001 From: Anh Do Date: Fri, 5 Apr 2024 00:34:59 -0400 Subject: [PATCH 11/22] Bad workaround for popover positioning issue --- ...etworkProtectionNavBarPopoverManager.swift | 4 +- .../Menu/StatusBarMenu.swift | 52 +++++++++++++------ 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionNavBarPopoverManager.swift b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionNavBarPopoverManager.swift index 089a82f3f1..a9282b784c 100644 --- a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionNavBarPopoverManager.swift +++ b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionNavBarPopoverManager.swift @@ -66,7 +66,7 @@ final class NetworkProtectionNavBarPopoverManager: NetPPopoverManager { // swiftlint:disable:next function_body_length func show(positionedBelow view: NSView, withDelegate delegate: NSPopoverDelegate) { - let popover = networkProtectionPopover ?? { + let popover = { let controller = NetworkProtectionIPCTunnelController(ipcClient: ipcClient) @@ -141,6 +141,7 @@ final class NetworkProtectionNavBarPopoverManager: NetPPopoverManager { func toggle(positionedBelow view: NSView, withDelegate delegate: NSPopoverDelegate) { if let networkProtectionPopover, networkProtectionPopover.isShown { networkProtectionPopover.close() + self.networkProtectionPopover = nil } else { let featureVisibility = DefaultNetworkProtectionVisibility() @@ -154,6 +155,7 @@ final class NetworkProtectionNavBarPopoverManager: NetPPopoverManager { func close() { networkProtectionPopover?.close() + networkProtectionPopover = nil } } #endif diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Menu/StatusBarMenu.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Menu/StatusBarMenu.swift index 1833f8482e..86a10b44aa 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Menu/StatusBarMenu.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Menu/StatusBarMenu.swift @@ -32,7 +32,17 @@ public final class StatusBarMenu: NSObject { private let model: StatusBarMenuModel private let statusItem: NSStatusItem - private let popover: NetworkProtectionPopover + private var popover: NetworkProtectionPopover? + + private let controller: TunnelController + private let statusReporter: NetworkProtectionStatusReporter + private let onboardingStatusPublisher: OnboardingStatusPublisher + private let appLauncher: AppLaunching + private let menuItems: () -> [MenuItem] + private let agentLoginItem: LoginItem? + private let isMenuBarStatusView: Bool + private let userDefaults: UserDefaults + private let uninstallHandler: () async -> Void // MARK: - NetP Icon publisher @@ -65,17 +75,15 @@ public final class StatusBarMenu: NSObject { self.statusItem = statusItem self.iconPublisher = NetworkProtectionIconPublisher(statusReporter: statusReporter, iconProvider: iconProvider) - popover = NetworkProtectionPopover(controller: controller, - onboardingStatusPublisher: onboardingStatusPublisher, - statusReporter: statusReporter, - appLauncher: appLauncher, - menuItems: menuItems, - agentLoginItem: agentLoginItem, - isMenuBarStatusView: isMenuBarStatusView, - userDefaults: userDefaults, - uninstallHandler: uninstallHandler) - - popover.behavior = .transient + self.controller = controller + self.statusReporter = statusReporter + self.onboardingStatusPublisher = onboardingStatusPublisher + self.appLauncher = appLauncher + self.menuItems = menuItems + self.agentLoginItem = agentLoginItem + self.isMenuBarStatusView = isMenuBarStatusView + self.userDefaults = userDefaults + self.uninstallHandler = uninstallHandler super.init() @@ -112,22 +120,34 @@ public final class StatusBarMenu: NSObject { // MARK: - Popover private func togglePopover(isOptionKeyPressed: Bool) { - if popover.isShown { + if let popover, popover.isShown { popover.close() + self.popover = nil } else { guard let button = statusItem.button else { return } - popover.setShowsDebugInformation(isOptionKeyPressed) - popover.show(relativeTo: button.bounds, of: button, preferredEdge: .maxY) + popover = NetworkProtectionPopover(controller: controller, + onboardingStatusPublisher: onboardingStatusPublisher, + statusReporter: statusReporter, + appLauncher: appLauncher, + menuItems: menuItems, + agentLoginItem: agentLoginItem, + isMenuBarStatusView: isMenuBarStatusView, + userDefaults: userDefaults, + uninstallHandler: uninstallHandler) + popover?.behavior = .transient + + popover?.setShowsDebugInformation(isOptionKeyPressed) + popover?.show(relativeTo: button.bounds, of: button, preferredEdge: .minY) } } // MARK: - Context private func showContextMenu() { - if popover.isShown { + if let popover, popover.isShown { popover.close() } From c0f64a1960c6c1b942873e616c9a9b00085150c5 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Fri, 5 Apr 2024 00:47:53 -0400 Subject: [PATCH 12/22] Remove unreliable check --- .../TunnelControllerView/TunnelControllerView.swift | 2 +- .../TunnelControllerViewModel.swift | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift index 219252d85a..e1239929c0 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift @@ -199,7 +199,7 @@ public struct TunnelControllerView: View { animationView.clipsToBounds = true } .playing(withIntro: .init( - skipIntro: model.isVPNEnabled && !model.isToggleDisabled && !model.isToggleUserInitiated, + skipIntro: model.isVPNEnabled && !model.isToggleDisabled, introStartFrame: 0, introEndFrame: 100, loopStartFrame: 130, diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift index ee7dea4518..7fd4ffc654 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift @@ -33,9 +33,6 @@ public final class TunnelControllerViewModel: ObservableObject { @Published public var isVPNEnabled = false - @Published - public var isToggleUserInitiated = false - /// The type of extension that's being used for NetP /// @Published @@ -231,14 +228,6 @@ public final class TunnelControllerViewModel: ObservableObject { } self.internalIsRunning = newValue - switch self.toggleTransition { - case .switchingOff(let locallyInitiated) where locallyInitiated: - self.isToggleUserInitiated = true - case .switchingOn(let locallyInitiated) where locallyInitiated: - self.isToggleUserInitiated = true - default: - self.isToggleUserInitiated = false - } if newValue { self.startNetworkProtection() From 7206235537d481a1c3e472dc50783ca1a4cace2d Mon Sep 17 00:00:00 2001 From: Anh Do Date: Fri, 5 Apr 2024 01:48:01 -0400 Subject: [PATCH 13/22] Fix typo --- .../NetworkProtectionUITests/NetworkProtectionAssetTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionAssetTests.swift b/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionAssetTests.swift index 50e900dbca..c407babeec 100644 --- a/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionAssetTests.swift +++ b/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionAssetTests.swift @@ -30,7 +30,7 @@ final class NetworkProtectionAssetTests: XCTestCase { let assetsAndExpectedRawValues: [NetworkProtectionAsset: String] = [ .ipAddressIcon: "IP-16", .serverLocationIcon: "Server-Location-16", - .vpnDisabledImage: "VPN-Disabled", + .vpnDisabledImage: "VPNDisabled", .vpnEnabledImage: "VPN", .vpnIcon: "VPN-16", .appleVaultIcon: "apple-vault-icon", From f78be2b78fe2b3e97087e28b0831aedfd9646d4b Mon Sep 17 00:00:00 2001 From: Anh Do Date: Mon, 8 Apr 2024 09:17:19 -0400 Subject: [PATCH 14/22] Add Lottie to notification target --- DuckDuckGo.xcodeproj/project.pbxproj | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index f6db85c0a7..fbe5fd9cc5 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3228,19 +3228,20 @@ BBDFDC5A2B2B8A0900F62D90 /* DataBrokerProtectionExternalWaitlistPixels.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBDFDC592B2B8A0900F62D90 /* DataBrokerProtectionExternalWaitlistPixels.swift */; }; BBDFDC5C2B2B8D7000F62D90 /* DataBrokerProtectionExternalWaitlistPixels.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBDFDC592B2B8A0900F62D90 /* DataBrokerProtectionExternalWaitlistPixels.swift */; }; BBDFDC5D2B2B8E2100F62D90 /* DataBrokerProtectionExternalWaitlistPixels.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBDFDC592B2B8A0900F62D90 /* DataBrokerProtectionExternalWaitlistPixels.swift */; }; - C1372EF42BBC5BAD003F8793 /* SecureTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1372EF32BBC5BAD003F8793 /* SecureTextField.swift */; }; - C1372EF52BBC5BAD003F8793 /* SecureTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1372EF32BBC5BAD003F8793 /* SecureTextField.swift */; }; - C1372EF62BBC5BAD003F8793 /* SecureTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1372EF32BBC5BAD003F8793 /* SecureTextField.swift */; }; BD384AC92BBC821A00EF3735 /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; BD384ACA2BBC821A00EF3735 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; BD384ACB2BBC821B00EF3735 /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; BD384ACC2BBC821B00EF3735 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; BD384ACD2BBC821D00EF3735 /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; BD384ACE2BBC821D00EF3735 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; + BD8A305D2BC425B000D0669F /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = BD8A305C2BC425B000D0669F /* Lottie */; }; BDE981D82BBCE4C700645880 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = BDE981D72BBCE4C700645880 /* Lottie */; }; BDE981D92BBD10D600645880 /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; BDE981DA2BBD10D600645880 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; BDE981DF2BBDBD0100645880 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = BDE981DE2BBDBD0100645880 /* Lottie */; }; + C1372EF42BBC5BAD003F8793 /* SecureTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1372EF32BBC5BAD003F8793 /* SecureTextField.swift */; }; + C1372EF52BBC5BAD003F8793 /* SecureTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1372EF32BBC5BAD003F8793 /* SecureTextField.swift */; }; + C1372EF62BBC5BAD003F8793 /* SecureTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1372EF32BBC5BAD003F8793 /* SecureTextField.swift */; }; C13909EF2B85FD4E001626ED /* AutofillActionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */; }; C13909F02B85FD4E001626ED /* AutofillActionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */; }; C13909F12B85FD4E001626ED /* AutofillActionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */; }; @@ -4931,6 +4932,7 @@ files = ( 7B624F172BA25C1F00A6C544 /* NetworkProtectionUI in Frameworks */, 37269F052B3332C2005E8E46 /* Common in Frameworks */, + BD8A305D2BC425B000D0669F /* Lottie in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -9133,6 +9135,7 @@ packageProductDependencies = ( 37269F042B3332C2005E8E46 /* Common */, 7B624F162BA25C1F00A6C544 /* NetworkProtectionUI */, + BD8A305C2BC425B000D0669F /* Lottie */, ); productName = DuckDuckGoNotifications; productReference = 4B4BEC202A11B4E2001D9AC5 /* DuckDuckGo Notifications.app */; @@ -15194,6 +15197,11 @@ package = B6F997B92B8F352500476735 /* XCRemoteSwiftPackageReference "apple-toolbox" */; productName = "plugin:SwiftLintPlugin"; }; + BD8A305C2BC425B000D0669F /* Lottie */ = { + isa = XCSwiftPackageProductDependency; + package = 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */; + productName = Lottie; + }; BDE981D72BBCE4C700645880 /* Lottie */ = { isa = XCSwiftPackageProductDependency; package = 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */; From 2864ba96534872fefb6fcfcd860ed7b12afb8b2e Mon Sep 17 00:00:00 2001 From: Anh Do <18567+quanganhdo@users.noreply.github.com> Date: Tue, 16 Apr 2024 09:10:51 -0400 Subject: [PATCH 15/22] VPN screen improvements #2: Location (#2590) Task/Issue URL: https://app.asana.com/0/72649045549333/1206942473732466/f Tech Design URL: CC: **Description**: Updates the location displayed on VPN screen **Steps to test this PR**: 1. Check the location section of the VPN screen 2. Make sure changing locations work --- DuckDuckGo.xcodeproj/project.pbxproj | 40 ++++++- .../xcshareddata/swiftpm/Package.resolved | 4 +- .../UserText+NetworkProtection.swift | 9 ++ DuckDuckGo/Localizable.xcstrings | 2 +- ...etworkProtectionNavBarPopoverManager.swift | 1 + .../DefaultVPNLocationFormatter.swift | 104 ++++++++++++++++++ ...tworkProtectionVPNCountryLabelsModel.swift | 9 +- DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift | 1 + .../UserText+NetworkProtectionUI.swift | 5 + .../Menu/StatusBarMenu.swift | 4 + .../NetworkProtectionAsset.swift | 1 + .../NetworkProtectionPopover.swift | 2 + .../SwiftUI/MenuItemCustomButton.swift | 88 +++++++++++++++ .../NetworkProtectionStatusViewModel.swift | 3 + .../TunnelControllerView.swift | 67 +++++++++-- .../TunnelControllerViewModel.swift | 40 ++++++- .../MockVPNLocationFormatter.swift | 36 ++++++ .../NetworkProtectionAssetTests.swift | 1 + .../NetworkProtectionStatusBarMenuTests.swift | 2 + .../TunnelControllerViewModelTests.swift | 14 ++- .../DefaultVPNLocationFormatterTests.swift | 90 +++++++++++++++ .../Mocks/MockVPNLocationFormatter.swift | 37 +++++++ 22 files changed, 541 insertions(+), 19 deletions(-) create mode 100644 DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/VPNLocation/DefaultVPNLocationFormatter.swift create mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/SwiftUI/MenuItemCustomButton.swift create mode 100644 LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/MockVPNLocationFormatter.swift create mode 100644 UnitTests/NetworkProtection/DefaultVPNLocationFormatterTests.swift create mode 100644 UnitTests/NetworkProtection/Mocks/MockVPNLocationFormatter.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 5ab306a610..de7412bbba 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3256,6 +3256,17 @@ BD384ACD2BBC821D00EF3735 /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; BD384ACE2BBC821D00EF3735 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; BD8A305D2BC425B000D0669F /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = BD8A305C2BC425B000D0669F /* Lottie */; }; + BDA7647C2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA7647B2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift */; }; + BDA7647D2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA7647B2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift */; }; + BDA7647E2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA7647B2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift */; }; + BDA7647F2BC4998900D0400C /* DefaultVPNLocationFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA7647B2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift */; }; + BDA764802BC4998A00D0400C /* DefaultVPNLocationFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA7647B2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift */; }; + BDA764842BC49E3F00D0400C /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */; }; + BDA764852BC49E4000D0400C /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */; }; + BDA7648D2BC4E4EF00D0400C /* DefaultVPNLocationFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA7648C2BC4E4EF00D0400C /* DefaultVPNLocationFormatterTests.swift */; }; + BDA7648E2BC4E4EF00D0400C /* DefaultVPNLocationFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA7648C2BC4E4EF00D0400C /* DefaultVPNLocationFormatterTests.swift */; }; + BDA764912BC4E57200D0400C /* MockVPNLocationFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA764902BC4E57200D0400C /* MockVPNLocationFormatter.swift */; }; + BDA764922BC4E57200D0400C /* MockVPNLocationFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA764902BC4E57200D0400C /* MockVPNLocationFormatter.swift */; }; BDE981D82BBCE4C700645880 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = BDE981D72BBCE4C700645880 /* Lottie */; }; BDE981D92BBD10D600645880 /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; BDE981DA2BBD10D600645880 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; @@ -4776,6 +4787,9 @@ BBDFDC592B2B8A0900F62D90 /* DataBrokerProtectionExternalWaitlistPixels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionExternalWaitlistPixels.swift; sourceTree = ""; }; BD384AC72BBC821100EF3735 /* vpn-light-mode.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "vpn-light-mode.json"; sourceTree = ""; }; BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "vpn-dark-mode.json"; sourceTree = ""; }; + BDA7647B2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultVPNLocationFormatter.swift; sourceTree = ""; }; + BDA7648C2BC4E4EF00D0400C /* DefaultVPNLocationFormatterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultVPNLocationFormatterTests.swift; sourceTree = ""; }; + BDA764902BC4E57200D0400C /* MockVPNLocationFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVPNLocationFormatter.swift; sourceTree = ""; }; C1372EF32BBC5BAD003F8793 /* SecureTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureTextField.swift; sourceTree = ""; }; C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillActionExecutor.swift; sourceTree = ""; }; C13909F32B85FD79001626ED /* AutofillDeleteAllPasswordsExecutorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillDeleteAllPasswordsExecutorTests.swift; sourceTree = ""; }; @@ -6466,10 +6480,12 @@ 4BCF15E32ABB987F0083F6DF /* NetworkProtection */ = { isa = PBXGroup; children = ( + BDA7648F2BC4E56200D0400C /* Mocks */, 4BCF15E62ABB98A20083F6DF /* Resources */, 4BCF15E42ABB98990083F6DF /* NetworkProtectionRemoteMessageTests.swift */, 4BD57C032AC112DF00B580EE /* NetworkProtectionRemoteMessagingTests.swift */, 7B09CBA72BA4BE7000CF245B /* NetworkProtectionPixelEventTests.swift */, + BDA7648C2BC4E4EF00D0400C /* DefaultVPNLocationFormatterTests.swift */, ); path = NetworkProtection; sourceTree = ""; @@ -8771,6 +8787,14 @@ path = View; sourceTree = ""; }; + BDA7648F2BC4E56200D0400C /* Mocks */ = { + isa = PBXGroup; + children = ( + BDA764902BC4E57200D0400C /* MockVPNLocationFormatter.swift */, + ); + path = Mocks; + sourceTree = ""; + }; BDE981DB2BBD110800645880 /* Assets */ = { isa = PBXGroup; children = ( @@ -8851,6 +8875,7 @@ EEA3EEAF2B24EB5100E8333A /* VPNLocation */ = { isa = PBXGroup; children = ( + BDA7647B2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift */, EEA3EEB22B24EC0600E8333A /* VPNLocationViewModel.swift */, EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */, EEC4A6682B2C87D300F7C0AA /* VPNLocationView.swift */, @@ -10772,6 +10797,7 @@ 3706FBCB293F65D500E42796 /* BookmarkHTMLImporter.swift in Sources */, 4BF97ADC2B43C5E200EB4240 /* VPNFeedbackSender.swift in Sources */, 987799F72999996B005D8EB6 /* BookmarkDatabase.swift in Sources */, + BDA7647D2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift in Sources */, 3706FBCC293F65D500E42796 /* CustomRoundedCornersShape.swift in Sources */, 3706FBCD293F65D500E42796 /* LocaleExtension.swift in Sources */, 3706FBCE293F65D500E42796 /* SavePaymentMethodViewController.swift in Sources */, @@ -11257,6 +11283,7 @@ 1DE03425298BC7F000CAB3D7 /* InternalUserDeciderStoreMock.swift in Sources */, 3706FE4A293F661700E42796 /* BookmarkManagedObjectTests.swift in Sources */, EEC8EB402982CD550065AA39 /* JSAlertViewModelTests.swift in Sources */, + BDA764922BC4E57200D0400C /* MockVPNLocationFormatter.swift in Sources */, 3706FE4B293F661700E42796 /* BookmarksHTMLImporterTests.swift in Sources */, 9FA75A3F2BA00E1400DA5FA6 /* BookmarksBarMenuFactoryTests.swift in Sources */, 56D145E929E6BB6300E3488A /* CapturingDataImportProvider.swift in Sources */, @@ -11352,6 +11379,7 @@ B603975229C1FFAD00902A34 /* ExpectedNavigationExtension.swift in Sources */, 3706FE85293F661700E42796 /* BWRequestTests.swift in Sources */, 3706FE86293F661700E42796 /* FileDownloadManagerTests.swift in Sources */, + BDA7648E2BC4E4EF00D0400C /* DefaultVPNLocationFormatterTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -11479,8 +11507,10 @@ 7BA7CC3E2AD11E380042E5CE /* TunnelControllerIPCService.swift in Sources */, 7BA7CC402AD11E3D0042E5CE /* AppLauncher+DefaultInitializer.swift in Sources */, 7B0694982B6E980F00FA4DBA /* VPNProxyLauncher.swift in Sources */, + BDA764842BC49E3F00D0400C /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */, EEC589DB2A4F1CE700BCD60C /* AppLauncher.swift in Sources */, B65DA5EF2A77CC3A00CBEE8D /* Bundle+NetworkProtectionExtensions.swift in Sources */, + BDA7647F2BC4998900D0400C /* DefaultVPNLocationFormatter.swift in Sources */, 4BF0E5072AD2551A00FFEC9E /* NetworkProtectionPixelEvent.swift in Sources */, 7BA7CC442AD11E490042E5CE /* UserText.swift in Sources */, 4BF0E5142AD25A2600FFEC9E /* DuckDuckGoUserAgent.swift in Sources */, @@ -11513,8 +11543,10 @@ 7BFE95592A9DF2AF0081ABE9 /* UserDefaults+NetworkProtectionWaitlist.swift in Sources */, 7BA7CC5C2AD120C30042E5CE /* EventMapping+NetworkProtectionError.swift in Sources */, B65DA5F02A77CC3C00CBEE8D /* Bundle+NetworkProtectionExtensions.swift in Sources */, + BDA764852BC49E4000D0400C /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */, 7BAF9E4D2A8A3CCB002D3B6E /* UserDefaults+NetworkProtectionShared.swift in Sources */, 7BA7CC392AD11E2D0042E5CE /* DuckDuckGoVPNAppDelegate.swift in Sources */, + BDA764802BC4998A00D0400C /* DefaultVPNLocationFormatter.swift in Sources */, 7BA7CC552AD11FFB0042E5CE /* NetworkProtectionOptionKeyExtension.swift in Sources */, 7BA7CC3D2AD11E380042E5CE /* TunnelControllerIPCService.swift in Sources */, 4BA7C4DA2B3F639800AFE511 /* NetworkProtectionTunnelController.swift in Sources */, @@ -11932,6 +11964,7 @@ 4B957A7C2AC7AE700062CA31 /* NSAlert+ActiveDownloadsTermination.swift in Sources */, B62B48412ADE48DE000DECE5 /* ArrayBuilder.swift in Sources */, 4B957A7D2AC7AE700062CA31 /* IndexPathExtension.swift in Sources */, + BDA7647E2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift in Sources */, 4B957A7E2AC7AE700062CA31 /* PasswordManagementNoteItemView.swift in Sources */, B6B4D1D22B0E0DD000C26286 /* DataImportNoDataView.swift in Sources */, 4B957A7F2AC7AE700062CA31 /* NSApplicationExtension.swift in Sources */, @@ -13075,6 +13108,7 @@ 1456D6E124EFCBC300775049 /* TabBarCollectionView.swift in Sources */, 4B4D60BF2A0C848A00BCD287 /* NetworkProtection+ConvenienceInitializers.swift in Sources */, 4B6B64842BA930420009FF9F /* WaitlistThankYouPromptPresenter.swift in Sources */, + BDA7647C2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift in Sources */, 3158B1592B0BF76400AF130C /* DataBrokerProtectionFeatureDisabler.swift in Sources */, B655124829A79465009BFE1C /* NavigationActionExtension.swift in Sources */, 9FA173EB2B7B232200EE4E6E /* AddEditBookmarkDialogView.swift in Sources */, @@ -13277,6 +13311,7 @@ B68412202B6A30680092F66A /* StringExtensionTests.swift in Sources */, B662D3D92755D7AD0035D4D6 /* PixelStoreTests.swift in Sources */, B6106BB526A809E60013B453 /* GeolocationProviderTests.swift in Sources */, + BDA764912BC4E57200D0400C /* MockVPNLocationFormatter.swift in Sources */, B6E6BA232BA2EDDE008AA7E1 /* FileReadResult.swift in Sources */, B6A5A2A025B96E8300AA7ADA /* AppStateChangePublisherTests.swift in Sources */, B63ED0E326B3E7FA00A9DAD1 /* CLLocationManagerMock.swift in Sources */, @@ -13355,6 +13390,7 @@ 373A1AB228451ED400586521 /* BookmarksHTMLImporterTests.swift in Sources */, 4B723E0626B0003E00E14D75 /* CSVParserTests.swift in Sources */, B60C6F8429B1BAD3007BFAA8 /* FileManagerTempDirReplacement.swift in Sources */, + BDA7648D2BC4E4EF00D0400C /* DefaultVPNLocationFormatterTests.swift in Sources */, 85F487B5276A8F2E003CE668 /* OnboardingTests.swift in Sources */, B626A7642992506A00053070 /* SerpHeadersNavigationResponderTests.swift in Sources */, AA652CCE25DD9071009059CC /* BookmarkListTests.swift in Sources */, @@ -14532,8 +14568,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { - kind = exactVersion; - version = 133.1.0; + branch = "anh/netp/location-selection"; + kind = branch; }; }; 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 517700a5d4..c07ef413be 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { - "revision" : "4699a5ff3d0669736e87f6da808884f245d80ede", - "version" : "133.1.0" + "branch" : "anh/netp/location-selection", + "revision" : "dc6725de9e7019ef7e2b0bb7438b9d98010808f8" } }, { diff --git a/DuckDuckGo/Common/Localizables/UserText+NetworkProtection.swift b/DuckDuckGo/Common/Localizables/UserText+NetworkProtection.swift index 2a5ec04f2d..317a7c5178 100644 --- a/DuckDuckGo/Common/Localizables/UserText+NetworkProtection.swift +++ b/DuckDuckGo/Common/Localizables/UserText+NetworkProtection.swift @@ -330,6 +330,15 @@ extension UserText { static let uninstallVPNAlertTitle = "Are you sure you want to uninstall the VPN?" // "vpn.uninstall.alert.informative.text" - Informative text for the alert that comes up when the user decides to uninstall our VPN static let uninstallVPNInformativeText = "Uninstalling the DuckDuckGo VPN will disconnect the VPN and remove it from your device." + + // MARK: - VPN Screen + // "network.protection.vpn.location.nearest" - Description of the location type in the VPN status view + static let netPVPNLocationNearest = "(Nearest)" + + // "network.protection.vpn.location.subtitle.formatted.city.and.country" - Subtitle for the preferred location item that formats a city and country. E.g Chicago, United States + static func netPVPNSettingsLocationSubtitleFormattedCityAndCountry(city: String, country: String) -> String { + return "\(city), \(country)" + } } #if DBP diff --git a/DuckDuckGo/Localizable.xcstrings b/DuckDuckGo/Localizable.xcstrings index 04f8d61b80..cf9456a461 100644 --- a/DuckDuckGo/Localizable.xcstrings +++ b/DuckDuckGo/Localizable.xcstrings @@ -52312,4 +52312,4 @@ } }, "version" : "1.0" -} +} \ No newline at end of file diff --git a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionNavBarPopoverManager.swift b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionNavBarPopoverManager.swift index f18edc61b7..fcf40ff007 100644 --- a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionNavBarPopoverManager.swift +++ b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionNavBarPopoverManager.swift @@ -114,6 +114,7 @@ final class NetworkProtectionNavBarPopoverManager: NetPPopoverManager { agentLoginItem: LoginItem.vpnMenu, isMenuBarStatusView: false, userDefaults: .netP, + locationFormatter: DefaultVPNLocationFormatter(), uninstallHandler: { [weak self] in _ = await self?.networkProtectionFeatureDisabler.disable(keepAuthToken: false, uninstallSystemExtension: true) }) diff --git a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/VPNLocation/DefaultVPNLocationFormatter.swift b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/VPNLocation/DefaultVPNLocationFormatter.swift new file mode 100644 index 0000000000..5f2bb50530 --- /dev/null +++ b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/VPNLocation/DefaultVPNLocationFormatter.swift @@ -0,0 +1,104 @@ +// +// DefaultVPNLocationFormatter.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// 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 SwiftUI +import NetworkProtection + +struct DefaultVPNLocationFormatter: VPNLocationFormatting { + func emoji(for country: String?, + preferredLocation someLocation: VPNSettings.SelectedLocation) -> String? { + if let country { + return NetworkProtectionVPNCountryLabelsModel(country: country, useFullCountryName: true).emoji + } + + let preferredLocation = VPNLocationModel(selectedLocation: someLocation) + switch preferredLocation.icon { + case .defaultIcon: + return nil + case .emoji(let emoji): + return emoji + } + } + + func string(from location: String?, + preferredLocation someLocation: VPNSettings.SelectedLocation) -> String { + let preferredLocation = VPNLocationModel(selectedLocation: someLocation) + + if let location { + return preferredLocation.isNearest ? "\(location) (Nearest)" : location + } + + return preferredLocation.title + } + + @available(macOS 12, *) + func string(from location: String?, + preferredLocation someLocation: VPNSettings.SelectedLocation, + locationTextColor: Color, + preferredLocationTextColor: Color) -> AttributedString { + let preferredLocation = VPNLocationModel(selectedLocation: someLocation) + + if let location { + var attributedString = AttributedString( + preferredLocation.isNearest ? "\(location) \(UserText.netPVPNLocationNearest)" : location + ) + attributedString.foregroundColor = locationTextColor + if let range = attributedString.range(of: UserText.netPVPNLocationNearest) { + attributedString[range].foregroundColor = preferredLocationTextColor + } + return attributedString + } + + var attributedString = AttributedString(preferredLocation.title) + attributedString.foregroundColor = locationTextColor + return attributedString + } +} + +final class VPNLocationModel: ObservableObject { + enum LocationIcon { + case defaultIcon + case emoji(String) + } + + let title: String + let icon: LocationIcon + let isNearest: Bool + + init(selectedLocation: VPNSettings.SelectedLocation) { + switch selectedLocation { + case .nearest: + title = UserText.vpnLocationNearestAvailable + icon = .defaultIcon + isNearest = true + case .location(let location): + let countryLabelsModel = NetworkProtectionVPNCountryLabelsModel(country: location.country, useFullCountryName: true) + if let city = location.city { + title = UserText.netPVPNSettingsLocationSubtitleFormattedCityAndCountry( + city: city, + country: countryLabelsModel.title + ) + } else { + title = countryLabelsModel.title + } + icon = .emoji(countryLabelsModel.emoji) + isNearest = false + } + } +} diff --git a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/VPNLocation/NetworkProtectionVPNCountryLabelsModel.swift b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/VPNLocation/NetworkProtectionVPNCountryLabelsModel.swift index 28c6a16b19..00a3f034ea 100644 --- a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/VPNLocation/NetworkProtectionVPNCountryLabelsModel.swift +++ b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/VPNLocation/NetworkProtectionVPNCountryLabelsModel.swift @@ -23,8 +23,13 @@ struct NetworkProtectionVPNCountryLabelsModel { let emoji: String let title: String - init(country: String) { - self.title = Locale.current.localizedString(forRegionCode: country) ?? country.capitalized + init(country: String, useFullCountryName: Bool = true) { + if useFullCountryName { + self.title = Locale.current.localizedString(forRegionCode: country) ?? country.capitalized + } else { + self.title = country.localizedUppercase + } + self.emoji = Self.flag(country: country) } diff --git a/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift b/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift index 67d2baeb1d..124ce85aec 100644 --- a/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift +++ b/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift @@ -256,6 +256,7 @@ final class DuckDuckGoVPNAppDelegate: NSObject, NSApplicationDelegate { agentLoginItem: nil, isMenuBarStatusView: true, userDefaults: .netP, + locationFormatter: DefaultVPNLocationFormatter(), uninstallHandler: { [weak self] in guard let self else { return } await self.vpnUninstaller.uninstall(includingSystemExtension: true) diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/UserText+NetworkProtectionUI.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/UserText+NetworkProtectionUI.swift index 174b133a09..0236302a96 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/UserText+NetworkProtectionUI.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Extensions/UserText+NetworkProtectionUI.swift @@ -29,6 +29,11 @@ final class UserText { static let networkProtectionStatusViewFeatureOn = NSLocalizedString("network.protection.status.view.feature.on", value: "DuckDuckGo VPN is ON", comment: "Text shown in NetworkProtection's status view when NetP is ON.") static let networkProtectionStatusViewTimerZero = "00:00:00" + static let netPVPNLocationNearest = NSLocalizedString("network.protection.vpn.location.nearest", value: "(Nearest)", comment: "Description of the location type in the VPN status view") + static let vpnLocationConnected = NSLocalizedString("network.protection.vpn.location.connected", value: "Connected Location", comment: "Description of the location type in the VPN status view") + static let vpnLocationSelected = NSLocalizedString("network.protection.vpn.location.selected", value: "Selected Location", comment: "Description of the location type in the VPN status view") + static let vpnDataVolume = NSLocalizedString("network.protection.vpn.data-volume", value: "Data Volume", comment: "Title for the data volume section in the VPN status view") + // MARK: - Onboarding static let networkProtectionOnboardingInstallExtensionTitle = NSLocalizedString("network.protection.onboarding.install.extension.title", value: "Install VPN System Extension", comment: "Title for the onboarding install-vpn-extension step") diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Menu/StatusBarMenu.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Menu/StatusBarMenu.swift index 86a10b44aa..5e02caa125 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Menu/StatusBarMenu.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Menu/StatusBarMenu.swift @@ -42,6 +42,7 @@ public final class StatusBarMenu: NSObject { private let agentLoginItem: LoginItem? private let isMenuBarStatusView: Bool private let userDefaults: UserDefaults + private let locationFormatter: VPNLocationFormatting private let uninstallHandler: () async -> Void // MARK: - NetP Icon publisher @@ -68,6 +69,7 @@ public final class StatusBarMenu: NSObject { agentLoginItem: LoginItem?, isMenuBarStatusView: Bool, userDefaults: UserDefaults, + locationFormatter: VPNLocationFormatting, uninstallHandler: @escaping () async -> Void) { self.model = model @@ -83,6 +85,7 @@ public final class StatusBarMenu: NSObject { self.agentLoginItem = agentLoginItem self.isMenuBarStatusView = isMenuBarStatusView self.userDefaults = userDefaults + self.locationFormatter = locationFormatter self.uninstallHandler = uninstallHandler super.init() @@ -136,6 +139,7 @@ public final class StatusBarMenu: NSObject { agentLoginItem: agentLoginItem, isMenuBarStatusView: isMenuBarStatusView, userDefaults: userDefaults, + locationFormatter: locationFormatter, uninstallHandler: uninstallHandler) popover?.behavior = .transient diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionAsset.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionAsset.swift index a811f9bab1..6e02224576 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionAsset.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionAsset.swift @@ -24,6 +24,7 @@ public enum NetworkProtectionAsset: String, CaseIterable { case vpnDisabledImage = "VPNDisabled" case vpnEnabledImage = "VPN" case vpnIcon = "VPN-16" + case nearestAvailable = "VPNLocation" // Apple Icons case appleVaultIcon = "apple-vault-icon" diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionPopover.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionPopover.swift index 073db45255..4236e963aa 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionPopover.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionPopover.swift @@ -58,6 +58,7 @@ public final class NetworkProtectionPopover: NSPopover { agentLoginItem: LoginItem?, isMenuBarStatusView: Bool, userDefaults: UserDefaults, + locationFormatter: VPNLocationFormatting, uninstallHandler: @escaping () async -> Void) { self.statusReporter = statusReporter @@ -70,6 +71,7 @@ public final class NetworkProtectionPopover: NSPopover { agentLoginItem: agentLoginItem, isMenuBarStatusView: isMenuBarStatusView, userDefaults: userDefaults, + locationFormatter: locationFormatter, uninstallHandler: uninstallHandler) super.init() diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/SwiftUI/MenuItemCustomButton.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/SwiftUI/MenuItemCustomButton.swift new file mode 100644 index 0000000000..cdd8bd79c8 --- /dev/null +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/SwiftUI/MenuItemCustomButton.swift @@ -0,0 +1,88 @@ +// +// MenuItemCustomButton.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// 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 SwiftUI + +struct MenuItemCustomButton: View { + @Environment(\.colorScheme) private var colorScheme + private var label: (Bool) -> Label + private let action: () async -> Void + + private let highlightAnimationStepSpeed = 0.05 + + @State private var isHovered = false + @State private var animatingTap = false + + init(action: @escaping () async -> Void, @ViewBuilder label: @escaping (Bool) -> Label) { + self.action = action + self.label = label + } + + var body: some View { + Button(action: { + buttonTapped() + }) { + HStack { + label(isHovered) + }.padding([.top, .bottom], 3) + .padding([.leading, .trailing], 9) + } + .frame(maxWidth: .infinity, alignment: .leading) + .background( + buttonBackground(highlighted: isHovered) + ) + .contentShape(Rectangle()) + .cornerRadius(4) + .onTapGesture { + buttonTapped() + } + .onHover { hovering in + if !animatingTap { + isHovered = hovering + } + } + .buttonStyle(PlainButtonStyle()) + } + + private func buttonBackground(highlighted: Bool) -> some View { + if highlighted { + return AnyView( + VisualEffectView(material: .selection, blendingMode: .withinWindow, state: .active, isEmphasized: true)) + } else { + return AnyView(Color.clear) + } + } + + private func buttonTapped() { + animatingTap = true + isHovered = false + + DispatchQueue.main.asyncAfter(deadline: .now() + highlightAnimationStepSpeed) { + isHovered = true + + DispatchQueue.main.asyncAfter(deadline: .now() + highlightAnimationStepSpeed) { + animatingTap = false + + Task { + await action() + } + } + } + } +} diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/StatusView/NetworkProtectionStatusViewModel.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/StatusView/NetworkProtectionStatusViewModel.swift index 7df51daf58..eb5509a68f 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/StatusView/NetworkProtectionStatusViewModel.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/StatusView/NetworkProtectionStatusViewModel.swift @@ -119,6 +119,7 @@ extension NetworkProtectionStatusView { isMenuBarStatusView: Bool, runLoopMode: RunLoop.Mode? = nil, userDefaults: UserDefaults, + locationFormatter: VPNLocationFormatting, uninstallHandler: @escaping () async -> Void) { self.tunnelController = controller @@ -135,6 +136,8 @@ extension NetworkProtectionStatusView { tunnelControllerViewModel = TunnelControllerViewModel(controller: tunnelController, onboardingStatusPublisher: onboardingStatusPublisher, statusReporter: statusReporter, + vpnSettings: .init(defaults: userDefaults), + locationFormatter: locationFormatter, appLauncher: appLauncher) connectionStatus = statusReporter.statusObserver.recentValue diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift index e1239929c0..96244b1539 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift @@ -28,6 +28,10 @@ fileprivate extension Font { .system(size: 13, weight: .regular, design: .default) } + static var location: Font { + .system(size: 13, weight: .regular, design: .default) + } + static var content: Font { .system(size: 13, weight: .regular, design: .default) } @@ -60,6 +64,10 @@ private enum Opacity { colorScheme == .light ? Double(0.6) : Double(0.5) } + static func location(colorScheme: ColorScheme) -> Double { + colorScheme == .light ? Double(0.6) : Double(0.5) + } + static let content = Double(0.58) static let label = Double(0.9) static let description = Double(0.9) @@ -85,6 +93,10 @@ fileprivate extension View { .foregroundColor(Color(.defaultText)) } + func applyLocationAttributes() -> some View { + font(.NetworkProtection.location) + } + func applyContentAttributes(colorScheme: ColorScheme) -> some View { opacity(Opacity.content) .font(.NetworkProtection.content) @@ -152,6 +164,8 @@ public struct TunnelControllerView: View { Divider() .padding(EdgeInsets(top: 5, leading: 9, bottom: 5, trailing: 9)) + locationView() + if model.showServerDetails { connectionStatusView() .disabled(on: !isEnabled) @@ -214,6 +228,50 @@ public struct TunnelControllerView: View { .frame(width: 8, height: 8) } + /// Connected/Selected location + /// + private func locationView() -> some View { + VStack(alignment: .leading, spacing: 0) { + Text(model.isVPNEnabled ? UserText.vpnLocationConnected : UserText.vpnLocationSelected) + .applySectionHeaderAttributes(colorScheme: colorScheme) + .padding(EdgeInsets(top: 6, leading: 9, bottom: 6, trailing: 9)) + + MenuItemCustomButton { + model.showLocationSettings() + dismiss() + } label: { isHovered in + HStack(alignment: .center, spacing: 10) { + if let emoji = model.emoji { + Text(emoji) + .font(.system(size: 13)) + .frame(width: 26, height: 26) + .background(Color(hex: "B2B2B2").opacity(0.3)) + .clipShape(Circle()) + } else if model.wantsNearestLocation { + Image(NetworkProtectionAsset.nearestAvailable) + .frame(width: 26, height: 26) + } + if #available(macOS 12, *) { + if isHovered { + Text(model.plainLocation) + .applyLocationAttributes() + .foregroundColor(.white) + } else { + Text(model.formattedLocation) + .applyLocationAttributes() + } + } else { + Text(model.plainLocation) + .applyLocationAttributes() + .foregroundColor(isHovered ? .white: Color(.defaultText)) + } + } + } + + dividerRow() + } + } + /// Connection status: server IP address and location /// private func connectionStatusView() -> some View { @@ -222,15 +280,6 @@ public struct TunnelControllerView: View { .applySectionHeaderAttributes(colorScheme: colorScheme) .padding(EdgeInsets(top: 6, leading: 9, bottom: 6, trailing: 9)) - MenuItemButton( - iconName: .serverLocationIcon, - title: UserText.networkProtectionStatusViewLocation, - detailTitle: model.serverLocation, - textColor: Color(.defaultText)) { - model.showLocationSettings() - dismiss() - }.applyMenuAttributes() - connectionStatusRow(icon: .ipAddressIcon, title: UserText.networkProtectionStatusViewIPAddress, details: model.serverAddress) diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift index 7fd4ffc654..dfd96cd277 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift @@ -52,6 +52,10 @@ public final class TunnelControllerViewModel: ObservableObject { /// private let statusReporter: NetworkProtectionStatusReporter + private let vpnSettings: VPNSettings + + private let locationFormatter: VPNLocationFormatting + private let appLauncher: AppLaunching // MARK: - Misc @@ -73,17 +77,22 @@ public final class TunnelControllerViewModel: ObservableObject { onboardingStatusPublisher: OnboardingStatusPublisher, statusReporter: NetworkProtectionStatusReporter, runLoopMode: RunLoop.Mode? = nil, + vpnSettings: VPNSettings, + locationFormatter: VPNLocationFormatting, appLauncher: AppLaunching) { self.tunnelController = controller self.onboardingStatusPublisher = onboardingStatusPublisher self.statusReporter = statusReporter self.runLoopMode = runLoopMode + self.vpnSettings = vpnSettings + self.locationFormatter = locationFormatter self.appLauncher = appLauncher connectionStatus = statusReporter.statusObserver.recentValue internalServerAddress = statusReporter.serverInfoObserver.recentValue.serverAddress - internalServerLocation = statusReporter.serverInfoObserver.recentValue.serverLocation?.serverLocation + internalServerAttributes = statusReporter.serverInfoObserver.recentValue.serverLocation + internalServerLocation = internalServerAttributes?.serverLocation // Particularly useful when unit testing with an initial status of our choosing. refreshInternalIsRunning() @@ -140,7 +149,8 @@ public final class TunnelControllerViewModel: ObservableObject { Task { @MainActor in self.internalServerAddress = serverInfo.serverAddress - self.internalServerLocation = serverInfo.serverLocation?.serverLocation + self.internalServerAttributes = serverInfo.serverLocation + self.internalServerLocation = self.internalServerAttributes?.serverLocation } } .store(in: &cancellables) @@ -431,6 +441,32 @@ public final class TunnelControllerViewModel: ObservableObject { } } + @Published + private var internalServerAttributes: NetworkProtectionServerInfo.ServerAttributes? + + var wantsNearestLocation: Bool { + guard case .nearest = vpnSettings.selectedLocation else { return false } + return true + } + + var emoji: String? { + locationFormatter.emoji(for: internalServerAttributes?.country, + preferredLocation: vpnSettings.selectedLocation) + } + + var plainLocation: String { + locationFormatter.string(from: internalServerLocation, + preferredLocation: vpnSettings.selectedLocation) + } + + @available(macOS 12, *) + var formattedLocation: AttributedString { + locationFormatter.string(from: internalServerLocation, + preferredLocation: vpnSettings.selectedLocation, + locationTextColor: Color(.defaultText), + preferredLocationTextColor: Color(.defaultText).opacity(0.6)) + } + // MARK: - Toggling VPN /// Start the VPN. diff --git a/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/MockVPNLocationFormatter.swift b/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/MockVPNLocationFormatter.swift new file mode 100644 index 0000000000..05abc170a5 --- /dev/null +++ b/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/MockVPNLocationFormatter.swift @@ -0,0 +1,36 @@ +// +// MockVPNLocationFormatter.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// 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 SwiftUI +import NetworkProtection + +struct MockVPNLocationFormatter: VPNLocationFormatting { + func emoji(for country: String?, preferredLocation someLocation: VPNSettings.SelectedLocation) -> String? { + nil + } + + func string(from location: String?, preferredLocation: NetworkProtection.VPNSettings.SelectedLocation) -> String { + "" + } + + @available(macOS 12, *) + func string(from location: String?, preferredLocation: NetworkProtection.VPNSettings.SelectedLocation, locationTextColor: Color, preferredLocationTextColor: Color) -> AttributedString { + "" + } +} diff --git a/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionAssetTests.swift b/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionAssetTests.swift index c407babeec..83d08e3ceb 100644 --- a/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionAssetTests.swift +++ b/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionAssetTests.swift @@ -33,6 +33,7 @@ final class NetworkProtectionAssetTests: XCTestCase { .vpnDisabledImage: "VPNDisabled", .vpnEnabledImage: "VPN", .vpnIcon: "VPN-16", + .nearestAvailable: "VPNLocation", .appleVaultIcon: "apple-vault-icon", .appleVPNIcon: "apple-vpn-icon", .appleSystemSettingsIcon: "apple-system-settings-icon", diff --git a/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionStatusBarMenuTests.swift b/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionStatusBarMenuTests.swift index 5b9e7e0dea..967f92c995 100644 --- a/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionStatusBarMenuTests.swift +++ b/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionStatusBarMenuTests.swift @@ -58,6 +58,7 @@ final class StatusBarMenuTests: XCTestCase { agentLoginItem: nil, isMenuBarStatusView: false, userDefaults: .standard, + locationFormatter: MockVPNLocationFormatter(), uninstallHandler: { }) menu.show() @@ -83,6 +84,7 @@ final class StatusBarMenuTests: XCTestCase { agentLoginItem: nil, isMenuBarStatusView: false, userDefaults: .standard, + locationFormatter: MockVPNLocationFormatter(), uninstallHandler: { }) menu.hide() diff --git a/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/TunnelControllerViewModelTests.swift b/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/TunnelControllerViewModelTests.swift index 83caf2cff6..884dc3d002 100644 --- a/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/TunnelControllerViewModelTests.swift +++ b/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/TunnelControllerViewModelTests.swift @@ -104,6 +104,8 @@ final class TunnelControllerViewModelTests: XCTestCase { controller: controller, onboardingStatusPublisher: Just(OnboardingStatus.completed).eraseToAnyPublisher(), statusReporter: statusReporter, + vpnSettings: .init(defaults: .standard), + locationFormatter: MockVPNLocationFormatter(), appLauncher: MockAppLauncher()) let isToggleOn = model.isToggleOn.wrappedValue @@ -124,6 +126,8 @@ final class TunnelControllerViewModelTests: XCTestCase { controller: controller, onboardingStatusPublisher: Just(OnboardingStatus.completed).eraseToAnyPublisher(), statusReporter: statusReporter, + vpnSettings: .init(defaults: .standard), + locationFormatter: MockVPNLocationFormatter(), appLauncher: MockAppLauncher()) XCTAssertEqual(model.connectionStatusDescription, UserText.networkProtectionStatusDisconnecting) @@ -151,6 +155,8 @@ final class TunnelControllerViewModelTests: XCTestCase { controller: controller, onboardingStatusPublisher: Just(OnboardingStatus.completed).eraseToAnyPublisher(), statusReporter: statusReporter, + vpnSettings: .init(defaults: .standard), + locationFormatter: MockVPNLocationFormatter(), appLauncher: MockAppLauncher()) let isToggleOn = model.isToggleOn.wrappedValue @@ -160,7 +166,7 @@ final class TunnelControllerViewModelTests: XCTestCase { XCTAssertEqual(model.featureStatusDescription, UserText.networkProtectionStatusViewFeatureOn) XCTAssertTrue(model.showServerDetails) XCTAssertEqual(model.serverAddress, mockServerIP) - XCTAssertEqual(model.serverLocation, "El Segundo, CA...") + XCTAssertEqual(model.serverLocation, "El Segundo, United States...") } /// We expect the model to properly reflect the connecting status. @@ -173,6 +179,8 @@ final class TunnelControllerViewModelTests: XCTestCase { controller: controller, onboardingStatusPublisher: Just(OnboardingStatus.completed).eraseToAnyPublisher(), statusReporter: statusReporter, + vpnSettings: .init(defaults: .standard), + locationFormatter: MockVPNLocationFormatter(), appLauncher: MockAppLauncher()) XCTAssertEqual(model.connectionStatusDescription, UserText.networkProtectionStatusConnecting) @@ -191,6 +199,8 @@ final class TunnelControllerViewModelTests: XCTestCase { controller: controller, onboardingStatusPublisher: Just(OnboardingStatus.completed).eraseToAnyPublisher(), statusReporter: statusReporter, + vpnSettings: .init(defaults: .standard), + locationFormatter: MockVPNLocationFormatter(), appLauncher: MockAppLauncher()) let networkProtectionWasStarted = expectation(description: "The model started the VPN when appropriate") @@ -221,6 +231,8 @@ final class TunnelControllerViewModelTests: XCTestCase { controller: controller, onboardingStatusPublisher: Just(OnboardingStatus.completed).eraseToAnyPublisher(), statusReporter: statusReporter, + vpnSettings: .init(defaults: .standard), + locationFormatter: MockVPNLocationFormatter(), appLauncher: MockAppLauncher()) let networkProtectionWasStopped = expectation(description: "The model stopped the VPN when appropriate") diff --git a/UnitTests/NetworkProtection/DefaultVPNLocationFormatterTests.swift b/UnitTests/NetworkProtection/DefaultVPNLocationFormatterTests.swift new file mode 100644 index 0000000000..0c52440181 --- /dev/null +++ b/UnitTests/NetworkProtection/DefaultVPNLocationFormatterTests.swift @@ -0,0 +1,90 @@ +// +// DefaultVPNLocationFormatterTests.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// 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 XCTest +@testable import DuckDuckGo_Privacy_Browser +@testable import NetworkProtection + +final class DefaultVPNLocationFormatterTests: XCTestCase { + private var formatter: DefaultVPNLocationFormatter! + + override func setUp() { + formatter = DefaultVPNLocationFormatter() + } + + func testUSLocation() { + let server = NetworkProtectionServerInfo.ServerAttributes(city: "Lafayette", country: "us", state: "la", timezoneOffset: 0) + let preferredLocation = VPNSettings.SelectedLocation.location(.init(country: "us")) + let otherPreferredLocation = VPNSettings.SelectedLocation.location(.init(country: "gb")) + + XCTAssertNil(formatter.emoji(for: nil, preferredLocation: .nearest)) + XCTAssertEqual(formatter.emoji(for: nil, preferredLocation: preferredLocation), "🇺🇸") + XCTAssertEqual(formatter.emoji(for: nil, preferredLocation: otherPreferredLocation), "🇬🇧") + XCTAssertEqual(formatter.emoji(for: server.country, preferredLocation: preferredLocation), "🇺🇸") + XCTAssertEqual(formatter.emoji(for: server.country, preferredLocation: otherPreferredLocation), "🇺🇸") + + XCTAssertEqual(formatter.string(from: nil, preferredLocation: .nearest), "Nearest available") + XCTAssertEqual(formatter.string(from: nil, preferredLocation: preferredLocation), "United States") + XCTAssertEqual(formatter.string(from: nil, preferredLocation: otherPreferredLocation), "United Kingdom") + XCTAssertEqual(formatter.string(from: server.serverLocation, preferredLocation: .nearest), "Lafayette, United States (Nearest)") + XCTAssertEqual(formatter.string(from: server.serverLocation, preferredLocation: preferredLocation), "Lafayette, United States") + XCTAssertEqual(formatter.string(from: server.serverLocation, preferredLocation: otherPreferredLocation), "Lafayette, United States") + + if #available(macOS 12, *) { + XCTAssertEqual(NSAttributedString(formatter.string(from: server.serverLocation, + preferredLocation: .nearest, + locationTextColor: .black, + preferredLocationTextColor: .black)).string, "Lafayette, United States (Nearest)") + XCTAssertEqual(NSAttributedString(formatter.string(from: server.serverLocation, + preferredLocation: preferredLocation, + locationTextColor: .black, + preferredLocationTextColor: .black)).string, "Lafayette, United States") + } + } + + func testCALocation() { + let server = NetworkProtectionServerInfo.ServerAttributes(city: "Toronto", country: "ca", state: "on", timezoneOffset: 0) + let preferredLocation = VPNSettings.SelectedLocation.location(.init(country: "ca")) + let otherPreferredLocation = VPNSettings.SelectedLocation.location(.init(country: "gb")) + + XCTAssertNil(formatter.emoji(for: nil, preferredLocation: .nearest)) + XCTAssertEqual(formatter.emoji(for: nil, preferredLocation: preferredLocation), "🇨🇦") + XCTAssertEqual(formatter.emoji(for: nil, preferredLocation: otherPreferredLocation), "🇬🇧") + XCTAssertEqual(formatter.emoji(for: server.country, preferredLocation: preferredLocation), "🇨🇦") + XCTAssertEqual(formatter.emoji(for: server.country, preferredLocation: otherPreferredLocation), "🇨🇦") + + XCTAssertEqual(formatter.string(from: nil, preferredLocation: .nearest), "Nearest available") + XCTAssertEqual(formatter.string(from: nil, preferredLocation: preferredLocation), "Canada") + XCTAssertEqual(formatter.string(from: nil, preferredLocation: otherPreferredLocation), "United Kingdom") + XCTAssertEqual(formatter.string(from: server.serverLocation, preferredLocation: .nearest), "Toronto, Canada (Nearest)") + XCTAssertEqual(formatter.string(from: server.serverLocation, preferredLocation: preferredLocation), "Toronto, Canada") + XCTAssertEqual(formatter.string(from: server.serverLocation, preferredLocation: otherPreferredLocation), "Toronto, Canada") + + if #available(macOS 12, *) { + XCTAssertEqual(NSAttributedString(formatter.string(from: server.serverLocation, + preferredLocation: .nearest, + locationTextColor: .black, + preferredLocationTextColor: .black)).string, "Toronto, Canada (Nearest)") + XCTAssertEqual(NSAttributedString(formatter.string(from: server.serverLocation, + preferredLocation: preferredLocation, + locationTextColor: .black, + preferredLocationTextColor: .black)).string, "Toronto, Canada") + } + } +} diff --git a/UnitTests/NetworkProtection/Mocks/MockVPNLocationFormatter.swift b/UnitTests/NetworkProtection/Mocks/MockVPNLocationFormatter.swift new file mode 100644 index 0000000000..579ba6e4ff --- /dev/null +++ b/UnitTests/NetworkProtection/Mocks/MockVPNLocationFormatter.swift @@ -0,0 +1,37 @@ +// +// MockVPNLocationFormatter.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// 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 SwiftUI +import NetworkProtection +@testable import DuckDuckGo_Privacy_Browser + +struct MockVPNLocationFormatter: VPNLocationFormatting { + func emoji(for country: String?, preferredLocation someLocation: VPNSettings.SelectedLocation) -> String? { + nil + } + + func string(from location: String?, preferredLocation: NetworkProtection.VPNSettings.SelectedLocation) -> String { + "" + } + + @available(macOS 12, *) + func string(from location: String?, preferredLocation: NetworkProtection.VPNSettings.SelectedLocation, locationTextColor: Color, preferredLocationTextColor: Color) -> AttributedString { + "" + } +} From c7ecdbbc1d5667dc87da54d8809aad25629a9ed2 Mon Sep 17 00:00:00 2001 From: Anh Do <18567+quanganhdo@users.noreply.github.com> Date: Fri, 19 Apr 2024 10:14:52 -0400 Subject: [PATCH 16/22] VPN screen improvements #3: Data volume + Menu items (#2611) Task/Issue URL: https://app.asana.com/0/0/1206942473732464/f Tech Design URL: CC: **Description**: Adds the Data Volume item & reorder the menu items. **Steps to test this PR**: 1. Open the VPN screen on either the status bar or main browser 2. Check if data volume is correct 3. Also, verify the menu item ordering. Open DDG should go last. --- ###### Internal references: [Pull Request Review Checklist](https://app.asana.com/0/1202500774821704/1203764234894239/f) [Software Engineering Expectations](https://app.asana.com/0/59792373528535/199064865822552) [Technical Design Template](https://app.asana.com/0/59792373528535/184709971311943) [Pull Request Documentation](https://app.asana.com/0/1202500774821704/1204012835277482/f) --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- DuckDuckGo/Application/URLEventHandler.swift | 2 + .../UserText+NetworkProtection.swift | 6 +- .../MainWindow/MainViewController.swift | 3 +- .../View/NetPPopoverManagerMock.swift | 6 ++ ...etworkProtectionNavBarPopoverManager.swift | 5 +- .../VPNMetadataCollector.swift | 3 +- .../View/WindowControllersManager.swift | 7 ++ DuckDuckGoDBPBackgroundAgent/UserText.swift | 4 +- DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift | 14 ++- .../TunnelControllerIPCService.swift | 10 +++ DuckDuckGoVPN/UserText.swift | 2 +- .../DataVolumeObserverThroughIPC.swift | 40 +++++++++ .../TunnelControllerIPCClient.swift | 20 ++++- .../TunnelControllerIPCServer.swift | 14 +++ .../NetworkProtectionAsset.swift | 4 +- .../NetworkProtectionColor.swift | 1 + .../SecondaryColor.colorset/Contents.json | 38 ++++++++ .../Icons/IP-16.imageset/Contents.json | 15 ---- .../Icons/IP-16.imageset/IP-16.pdf | Bin 2306 -> 0 bytes .../Server-Location-16.imageset/Contents.json | 15 ---- .../Server-Location-16.pdf | Bin 5417 -> 0 bytes .../Icons/VPNDownload.imageset/Contents.json | 2 +- .../VPNDownload.imageset/vpn-download.pdf | Bin 1357 -> 1351 bytes .../Icons/VPNLocation.imageset/Contents.json | 3 - .../VPNLocation.imageset/VPNLocation.pdf | Bin 1497 -> 2495 bytes .../Icons/VPNUpload.imageset/Contents.json | 2 +- .../Icons/VPNUpload.imageset/vpn-upload.pdf | Bin 1361 -> 1355 bytes .../TunnelControllerView.swift | 81 +++++++++++++----- .../TunnelControllerViewModel.swift | 44 ++++++++-- .../NetworkProtectionAssetTests.swift | 4 +- .../TunnelControllerViewModelTests.swift | 26 +++++- .../DefaultVPNLocationFormatterTests.swift | 4 +- 34 files changed, 296 insertions(+), 85 deletions(-) create mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionIPC/Observers/DataVolumeObserverThroughIPC.swift create mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Colors/SecondaryColor.colorset/Contents.json delete mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/IP-16.imageset/Contents.json delete mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/IP-16.imageset/IP-16.pdf delete mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/Server-Location-16.imageset/Contents.json delete mode 100644 LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/Server-Location-16.imageset/Server-Location-16.pdf diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index de7412bbba..c7f43fd49d 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -14568,7 +14568,7 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { - branch = "anh/netp/location-selection"; + branch = "anh/netp/screen-improvements"; kind = branch; }; }; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index c07ef413be..dcc3605227 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { - "branch" : "anh/netp/location-selection", - "revision" : "dc6725de9e7019ef7e2b0bb7438b9d98010808f8" + "branch" : "anh/netp/screen-improvements", + "revision" : "3b354a70cdfff5d46d2c8f525748dee63a36ad88" } }, { diff --git a/DuckDuckGo/Application/URLEventHandler.swift b/DuckDuckGo/Application/URLEventHandler.swift index e261efdf12..a0f4956d8c 100644 --- a/DuckDuckGo/Application/URLEventHandler.swift +++ b/DuckDuckGo/Application/URLEventHandler.swift @@ -145,6 +145,8 @@ final class URLEventHandler { WindowControllersManager.shared.showPreferencesTab(withSelectedPane: .vpn) case AppLaunchCommand.shareFeedback.launchURL: WindowControllersManager.shared.showShareFeedbackModal() + case AppLaunchCommand.justOpen.launchURL: + WindowControllersManager.shared.showMainWindow() case AppLaunchCommand.showVPNLocations.launchURL: WindowControllersManager.shared.showPreferencesTab(withSelectedPane: .vpn) WindowControllersManager.shared.showLocationPickerSheet() diff --git a/DuckDuckGo/Common/Localizables/UserText+NetworkProtection.swift b/DuckDuckGo/Common/Localizables/UserText+NetworkProtection.swift index 317a7c5178..4dd17342ea 100644 --- a/DuckDuckGo/Common/Localizables/UserText+NetworkProtection.swift +++ b/DuckDuckGo/Common/Localizables/UserText+NetworkProtection.swift @@ -42,8 +42,8 @@ extension UserText { static let networkProtectionInviteSuccessMessage = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere." // MARK: - Navigation Bar Status View - // "network.protection.navbar.status.view.share.feedback" - Menu item for 'Send VPN Feedback' in the VPN status view that's shown in the navigation bar - static let networkProtectionNavBarStatusViewShareFeedback = "Send VPN Feedback…" + // "network.protection.navbar.status.view.share.feedback" - Menu item for 'Share VPN Feedback' in the VPN status view that's shown in the navigation bar + static let networkProtectionNavBarStatusViewShareFeedback = "Share VPN Feedback…" // "network.protection.status.menu.vpn.settings" - The status menu 'VPN Settings' menu item static let networkProtectionNavBarStatusMenuVPNSettings = "VPN Settings…" // "network.protection.status.menu.faq" - The status menu 'FAQ' menu item @@ -299,7 +299,7 @@ extension UserText { // "vpn.location.description.nearest" - Nearest city setting description static let vpnLocationNearest = "Nearest" // "vpn.location.description.nearest.available" - Nearest available location setting description - static let vpnLocationNearestAvailable = "Nearest available" + static let vpnLocationNearestAvailable = "Nearest Location" // "vpn.location.nearest.available.title" - Subtitle underneath the nearest available vpn location preference text. static let vpnLocationNearestAvailableSubtitle = "Automatically connect to the nearest server we can find." diff --git a/DuckDuckGo/MainWindow/MainViewController.swift b/DuckDuckGo/MainWindow/MainViewController.swift index 04b5810087..f2b57e1c54 100644 --- a/DuckDuckGo/MainWindow/MainViewController.swift +++ b/DuckDuckGo/MainWindow/MainViewController.swift @@ -93,7 +93,8 @@ final class MainViewController: NSViewController { serverInfoObserver: ipcClient.ipcServerInfoObserver, connectionErrorObserver: ipcClient.ipcConnectionErrorObserver, connectivityIssuesObserver: connectivityIssuesObserver, - controllerErrorMessageObserver: controllerErrorMessageObserver + controllerErrorMessageObserver: controllerErrorMessageObserver, + dataVolumeObserver: ipcClient.ipcDataVolumeObserver ) }() diff --git a/DuckDuckGo/NavigationBar/View/NetPPopoverManagerMock.swift b/DuckDuckGo/NavigationBar/View/NetPPopoverManagerMock.swift index 0af23bdcd5..0007a3e54a 100644 --- a/DuckDuckGo/NavigationBar/View/NetPPopoverManagerMock.swift +++ b/DuckDuckGo/NavigationBar/View/NetPPopoverManagerMock.swift @@ -63,6 +63,12 @@ final class IPCClientMock: NetworkProtectionIPCClient { } var ipcControllerErrorMessageObserver: any NetworkProtection.ControllerErrorMesssageObserver = ControllerErrorMesssageObserverMock() + final class DataVolumeObserverMock: NetworkProtection.DataVolumeObserver { + var publisher: AnyPublisher = PassthroughSubject().eraseToAnyPublisher() + var recentValue: DataVolume = .init() + } + var ipcDataVolumeObserver: any NetworkProtection.DataVolumeObserver = DataVolumeObserverMock() + func start() {} func stop() {} diff --git a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionNavBarPopoverManager.swift b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionNavBarPopoverManager.swift index fcf40ff007..27bacb11ad 100644 --- a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionNavBarPopoverManager.swift +++ b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionNavBarPopoverManager.swift @@ -32,6 +32,7 @@ protocol NetworkProtectionIPCClient { var ipcStatusObserver: ConnectionStatusObserver { get } var ipcServerInfoObserver: ConnectionServerInfoObserver { get } var ipcConnectionErrorObserver: ConnectionErrorObserver { get } + var ipcDataVolumeObserver: DataVolumeObserver { get } func start() func stop() @@ -41,6 +42,7 @@ extension TunnelControllerIPCClient: NetworkProtectionIPCClient { public var ipcStatusObserver: any NetworkProtection.ConnectionStatusObserver { connectionStatusObserver } public var ipcServerInfoObserver: any NetworkProtection.ConnectionServerInfoObserver { serverInfoObserver } public var ipcConnectionErrorObserver: any NetworkProtection.ConnectionErrorObserver { connectionErrorObserver } + public var ipcDataVolumeObserver: any NetworkProtection.DataVolumeObserver { dataVolumeObserver } } final class NetworkProtectionNavBarPopoverManager: NetPPopoverManager { @@ -69,7 +71,8 @@ final class NetworkProtectionNavBarPopoverManager: NetPPopoverManager { serverInfoObserver: ipcClient.ipcServerInfoObserver, connectionErrorObserver: ipcClient.ipcConnectionErrorObserver, connectivityIssuesObserver: ConnectivityIssueObserverThroughDistributedNotifications(), - controllerErrorMessageObserver: ControllerErrorMesssageObserverThroughDistributedNotifications() + controllerErrorMessageObserver: ControllerErrorMesssageObserverThroughDistributedNotifications(), + dataVolumeObserver: ipcClient.ipcDataVolumeObserver ) let onboardingStatusPublisher = UserDefaults.netP.networkProtectionOnboardingStatusPublisher diff --git a/DuckDuckGo/VPNFeedbackForm/VPNMetadataCollector.swift b/DuckDuckGo/VPNFeedbackForm/VPNMetadataCollector.swift index c24d64bf6e..b4e463df17 100644 --- a/DuckDuckGo/VPNFeedbackForm/VPNMetadataCollector.swift +++ b/DuckDuckGo/VPNFeedbackForm/VPNMetadataCollector.swift @@ -121,7 +121,8 @@ final class DefaultVPNMetadataCollector: VPNMetadataCollector { serverInfoObserver: ipcClient.serverInfoObserver, connectionErrorObserver: ipcClient.connectionErrorObserver, connectivityIssuesObserver: ConnectivityIssueObserverThroughDistributedNotifications(), - controllerErrorMessageObserver: ControllerErrorMesssageObserverThroughDistributedNotifications() + controllerErrorMessageObserver: ControllerErrorMesssageObserverThroughDistributedNotifications(), + dataVolumeObserver: ipcClient.dataVolumeObserver ) // Force refresh just in case. A refresh is requested when the IPC client is created, but distributed notifications don't guarantee delivery diff --git a/DuckDuckGo/Windows/View/WindowControllersManager.swift b/DuckDuckGo/Windows/View/WindowControllersManager.swift index ae37e64c41..10758ce56d 100644 --- a/DuckDuckGo/Windows/View/WindowControllersManager.swift +++ b/DuckDuckGo/Windows/View/WindowControllersManager.swift @@ -237,6 +237,13 @@ extension WindowControllersManager { } } + func showMainWindow() { + guard WindowControllersManager.shared.lastKeyMainWindowController == nil else { return } + let tabCollection = TabCollection(tabs: []) + let tabCollectionViewModel = TabCollectionViewModel(tabCollection: tabCollection) + _ = WindowsManager.openNewWindow(with: tabCollectionViewModel) + } + func showLocationPickerSheet() { let locationsViewController = VPNLocationsHostingViewController() let locationsWindowController = locationsViewController.wrappedInWindowController() diff --git a/DuckDuckGoDBPBackgroundAgent/UserText.swift b/DuckDuckGoDBPBackgroundAgent/UserText.swift index 5b460321e5..608cab9e14 100644 --- a/DuckDuckGoDBPBackgroundAgent/UserText.swift +++ b/DuckDuckGoDBPBackgroundAgent/UserText.swift @@ -21,6 +21,6 @@ import Foundation final class UserText { // MARK: - Status Menu - static let networkProtectionStatusMenuShareFeedback = NSLocalizedString("network.protection.status.menu.share.feedback", value: "Send VPN Feedback...", comment: "The status menu 'Send VPN Feedback' menu item") - static let networkProtectionStatusMenuOpenDuckDuckGo = NSLocalizedString("network.protection.status.menu.open.duckduckgo", value: "Open DuckDuckGo...", comment: "The status menu 'Open DuckDuckGo' menu item") + static let networkProtectionStatusMenuShareFeedback = NSLocalizedString("network.protection.status.menu.share.feedback", value: "Share VPN Feedback…", comment: "The status menu 'Share VPN Feedback' menu item") + static let networkProtectionStatusMenuOpenDuckDuckGo = NSLocalizedString("network.protection.status.menu.open.duckduckgo", value: "Open DuckDuckGo…", comment: "The status menu 'Open DuckDuckGo' menu item") } diff --git a/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift b/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift index 124ce85aec..034d768892 100644 --- a/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift +++ b/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift @@ -187,12 +187,18 @@ final class DuckDuckGoVPNAppDelegate: NSObject, NSApplicationDelegate { platformNotificationCenter: NSWorkspace.shared.notificationCenter, platformDidWakeNotification: NSWorkspace.didWakeNotification) + let dataVolumeObserver = DataVolumeObserverThroughSession( + tunnelSessionProvider: tunnelController, + platformNotificationCenter: NSWorkspace.shared.notificationCenter, + platformDidWakeNotification: NSWorkspace.didWakeNotification) + return DefaultNetworkProtectionStatusReporter( statusObserver: statusObserver, serverInfoObserver: serverInfoObserver, connectionErrorObserver: errorObserver, connectivityIssuesObserver: DisabledConnectivityIssueObserver(), - controllerErrorMessageObserver: ControllerErrorMesssageObserverThroughDistributedNotifications() + controllerErrorMessageObserver: ControllerErrorMesssageObserverThroughDistributedNotifications(), + dataVolumeObserver: dataVolumeObserver ) }() @@ -245,12 +251,12 @@ final class DuckDuckGoVPNAppDelegate: NSObject, NSApplicationDelegate { StatusBarMenu.MenuItem(name: UserText.networkProtectionStatusMenuFAQ, action: { [weak self] in await self?.appLauncher.launchApp(withCommand: .showFAQ) }), + StatusBarMenu.MenuItem(name: UserText.networkProtectionStatusMenuShareFeedback, action: { [weak self] in + await self?.appLauncher.launchApp(withCommand: .shareFeedback) + }), StatusBarMenu.MenuItem(name: UserText.networkProtectionStatusMenuOpenDuckDuckGo, action: { [weak self] in await self?.appLauncher.launchApp(withCommand: .justOpen) }), - StatusBarMenu.MenuItem(name: UserText.networkProtectionStatusMenuShareFeedback, action: { [weak self] in - await self?.appLauncher.launchApp(withCommand: .shareFeedback) - }) ] }, agentLoginItem: nil, diff --git a/DuckDuckGoVPN/TunnelControllerIPCService.swift b/DuckDuckGoVPN/TunnelControllerIPCService.swift index c8a9ce456d..ad0faba0cb 100644 --- a/DuckDuckGoVPN/TunnelControllerIPCService.swift +++ b/DuckDuckGoVPN/TunnelControllerIPCService.swift @@ -50,6 +50,7 @@ final class TunnelControllerIPCService { subscribeToErrorChanges() subscribeToStatusUpdates() subscribeToServerChanges() + subscribeToDataVolumeUpdates() server.serverDelegate = self } @@ -84,6 +85,15 @@ final class TunnelControllerIPCService { } .store(in: &cancellables) } + + private func subscribeToDataVolumeUpdates() { + statusReporter.dataVolumeObserver.publisher + .subscribe(on: DispatchQueue.main) + .sink { [weak self] dataVolume in + self?.server.dataVolumeUpdated(dataVolume) + } + .store(in: &cancellables) + } } // MARK: - Requests from the client diff --git a/DuckDuckGoVPN/UserText.swift b/DuckDuckGoVPN/UserText.swift index 57ea5e3c3b..4c64c664eb 100644 --- a/DuckDuckGoVPN/UserText.swift +++ b/DuckDuckGoVPN/UserText.swift @@ -24,5 +24,5 @@ final class UserText { static let networkProtectionStatusMenuVPNSettings = NSLocalizedString("network.protection.status.menu.vpn.settings", value: "VPN Settings…", comment: "The status menu 'VPN Settings' menu item") static let networkProtectionStatusMenuFAQ = NSLocalizedString("network.protection.status.menu.faq", value: "Frequently Asked Questions…", comment: "The status menu 'FAQ' menu item") static let networkProtectionStatusMenuOpenDuckDuckGo = NSLocalizedString("network.protection.status.menu.vpn.open-duckduckgo", value: "Open DuckDuckGo…", comment: "The status menu 'Open DuckDuckGo' menu item") - static let networkProtectionStatusMenuShareFeedback = NSLocalizedString("network.protection.status.menu.share.feedback", value: "Send VPN Feedback…", comment: "The status menu 'Send VPN Feedback' menu item") + static let networkProtectionStatusMenuShareFeedback = NSLocalizedString("network.protection.status.menu.share.feedback", value: "Share VPN Feedback…", comment: "The status menu 'Share VPN Feedback' menu item") } diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionIPC/Observers/DataVolumeObserverThroughIPC.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionIPC/Observers/DataVolumeObserverThroughIPC.swift new file mode 100644 index 0000000000..ee605da45a --- /dev/null +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionIPC/Observers/DataVolumeObserverThroughIPC.swift @@ -0,0 +1,40 @@ +// +// DataVolumeObserverThroughIPC.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// 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 Combine +import Foundation +import NetworkProtection + +public final class DataVolumeObserverThroughIPC: DataVolumeObserver { + + private let subject = CurrentValueSubject(.init()) + + // MARK: - DataVolumeObserver + + public lazy var publisher = subject.eraseToAnyPublisher() + + public var recentValue: DataVolume { + subject.value + } + + // MARK: - Publishing Updates + + func publish(_ dataVolume: DataVolume) { + subject.send(dataVolume) + } +} diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionIPC/TunnelControllerIPCClient.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionIPC/TunnelControllerIPCClient.swift index 769939f65e..695218ab96 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionIPC/TunnelControllerIPCClient.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionIPC/TunnelControllerIPCClient.swift @@ -26,6 +26,7 @@ public protocol IPCClientInterface: AnyObject { func errorChanged(_ error: String?) func serverInfoChanged(_ serverInfo: NetworkProtectionStatusServerInfo) func statusChanged(_ status: ConnectionStatus) + func dataVolumeUpdated(_ dataVolume: DataVolume) } /// This is the XPC interface with parameters that can be packed properly @@ -34,6 +35,7 @@ protocol XPCClientInterface { func errorChanged(error: String?) func serverInfoChanged(payload: Data) func statusChanged(payload: Data) + func dataVolumeUpdated(payload: Data) } public final class TunnelControllerIPCClient { @@ -47,6 +49,7 @@ public final class TunnelControllerIPCClient { public var serverInfoObserver = ConnectionServerInfoObserverThroughIPC() public var connectionErrorObserver = ConnectionErrorObserverThroughIPC() public var connectionStatusObserver = ConnectionStatusObserverThroughIPC() + public var dataVolumeObserver = DataVolumeObserverThroughIPC() /// The delegate. /// @@ -65,7 +68,8 @@ public final class TunnelControllerIPCClient { clientDelegate: self.clientDelegate, serverInfoObserver: self.serverInfoObserver, connectionErrorObserver: self.connectionErrorObserver, - connectionStatusObserver: self.connectionStatusObserver + connectionStatusObserver: self.connectionStatusObserver, + dataVolumeObserver: self.dataVolumeObserver ) xpc = XPCClient( @@ -97,15 +101,18 @@ private final class TunnelControllerXPCClientDelegate: XPCClientInterface { let serverInfoObserver: ConnectionServerInfoObserverThroughIPC let connectionErrorObserver: ConnectionErrorObserverThroughIPC let connectionStatusObserver: ConnectionStatusObserverThroughIPC + let dataVolumeObserver: DataVolumeObserverThroughIPC init(clientDelegate: IPCClientInterface?, serverInfoObserver: ConnectionServerInfoObserverThroughIPC, connectionErrorObserver: ConnectionErrorObserverThroughIPC, - connectionStatusObserver: ConnectionStatusObserverThroughIPC) { + connectionStatusObserver: ConnectionStatusObserverThroughIPC, + dataVolumeObserver: DataVolumeObserverThroughIPC) { self.clientDelegate = clientDelegate self.serverInfoObserver = serverInfoObserver self.connectionErrorObserver = connectionErrorObserver self.connectionStatusObserver = connectionStatusObserver + self.dataVolumeObserver = dataVolumeObserver } func errorChanged(error: String?) { @@ -130,6 +137,15 @@ private final class TunnelControllerXPCClientDelegate: XPCClientInterface { connectionStatusObserver.publish(status) clientDelegate?.statusChanged(status) } + + func dataVolumeUpdated(payload: Data) { + guard let dataVolume = try? JSONDecoder().decode(DataVolume.self, from: payload) else { + return + } + + dataVolumeObserver.publish(dataVolume) + clientDelegate?.dataVolumeUpdated(dataVolume) + } } // MARK: - Outgoing communication to the server diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionIPC/TunnelControllerIPCServer.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionIPC/TunnelControllerIPCServer.swift index 3fe62c74f3..13ee59a0d3 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionIPC/TunnelControllerIPCServer.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionIPC/TunnelControllerIPCServer.swift @@ -135,6 +135,20 @@ extension TunnelControllerIPCServer: IPCClientInterface { client.statusChanged(payload: payload) } } + + public func dataVolumeUpdated(_ dataVolume: DataVolume) { + let payload: Data + + do { + payload = try JSONEncoder().encode(dataVolume) + } catch { + return + } + + xpc.forEachClient { client in + client.dataVolumeUpdated(payload: payload) + } + } } // MARK: - Incoming communication from a client diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionAsset.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionAsset.swift index 6e02224576..a8288b7dbd 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionAsset.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionAsset.swift @@ -19,12 +19,12 @@ import Foundation public enum NetworkProtectionAsset: String, CaseIterable { - case ipAddressIcon = "IP-16" - case serverLocationIcon = "Server-Location-16" case vpnDisabledImage = "VPNDisabled" case vpnEnabledImage = "VPN" case vpnIcon = "VPN-16" case nearestAvailable = "VPNLocation" + case dataReceived = "VPNDownload" + case dataSent = "VPNUpload" // Apple Icons case appleVaultIcon = "apple-vault-icon" diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionColor.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionColor.swift index 010085527a..2145e2da36 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionColor.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/NetworkProtectionColor.swift @@ -31,6 +31,7 @@ extension Color { /// enum NetworkProtectionColor: String { case defaultText = "TextColor" + case secondaryText = "SecondaryColor" case linkColor = "LinkBlueColor" case onboardingButtonBackgroundColor = "OnboardingButtonBackgroundColor" #if swift(<5.9) diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Colors/SecondaryColor.colorset/Contents.json b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Colors/SecondaryColor.colorset/Contents.json new file mode 100644 index 0000000000..8f7e96555c --- /dev/null +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Colors/SecondaryColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.600", + "blue" : "0x00", + "green" : "0x00", + "red" : "0x00" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.850", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/IP-16.imageset/Contents.json b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/IP-16.imageset/Contents.json deleted file mode 100644 index 657dbb1f2c..0000000000 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/IP-16.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "filename" : "IP-16.pdf", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "template-rendering-intent" : "template" - } -} diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/IP-16.imageset/IP-16.pdf b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/IP-16.imageset/IP-16.pdf deleted file mode 100644 index f68365bd0a32b0df9ab73a1f2fe798fffde1ffe0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2306 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 M%e!~K`op*X2D&^hD*ylh diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNDownload.imageset/Contents.json b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNDownload.imageset/Contents.json index c9df096cfc..e030aab433 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNDownload.imageset/Contents.json +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNDownload.imageset/Contents.json @@ -10,6 +10,6 @@ "version" : 1 }, "properties" : { - "template-rendering-intent" : "template" + "template-rendering-intent" : "original" } } diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNDownload.imageset/vpn-download.pdf b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNDownload.imageset/vpn-download.pdf index 55844a6647538a3ec000cd7c8e36e8f014467691..d5288602de6bba00209b51321e5dfb6fcd53f99d 100644 GIT binary patch literal 1351 zcmZXUOK;mS49D;K6ucB@50&+%1QZ3<^nqa;mZiHDJ9v(oW=QPKc86}CzLeuvS|Lc} zpGAuNlg!ET`u1FMWK4p9>gO*8aB%_I%}aZC-{x&RJjw2-$U$%?(t^v&!-vIkoy?dz zvi}^*P5T6z;EE2irmbVXXW4c9yDj4C?g|$7^QWR=e^_=SVbd_lyDJ2oi(xlz#&>FZ z6*O8cq1dzW*!xXWlnl97289H}F(}jy_Qb4rs03Ip3`&iFTnTSN=!mkxfa3uTI-KkZ zX1EPO2@Q%Xs}%~^O)PMuWKi~)Av)y(Ini1gG$1KTr%Z?Xj3-nu&7JdBx!F0*jSv_d z1l~FGFeCky$*?~)3ypMBR%tb*up_lFNK100)Vo9lSq4E`&WJ>-NQ_=Ggo35ASZ}l@ zXpQn1x=Ia5Y9nNzPU$T*_ufcH1)|6-T0w?_D-*oaBoMt89-SrDiD1dA3{vSMV(2^- zV6-cyVvhcI%uj*!QOrmes;Bb4ZGwV5u}4<2Z!9%)->E}erw%@hg+nsA`p6enukj8V>yIWLc z8%w$|&D;Q&c})yRwGPqD4JjlvP4=hlnnHz?!3u_Z7S%J^%6IWML}s4ck*AbkGhR3) zzR25rS8a|>U+VbC1Ry(fdin-uEmfItza*)W7L+5RF?oYLHSAlYs7h%~hJl9U7)e1h zI-s`Bx4T%gRK1VaB*$q}?c*7oZr5iAQL$@vhJuJT!4g1Bi8-#7ckasQwoQv@`U8dh8S9DMT zk)S*x6bCjv_V&^gB?}y-_ZYx%6VRZ51M}P)M5l;(?-l8Iu0;rFJEBBKP&p|Vb_FwB zS>u!g$*uG*1lZ3E1rI?cizP%7HdY}C3!{{GfK+)EjFOrAl8 zbWoeoMNH*8#JRGm71m2SdT6}rl?(1 z%Nc3zB|$%1YHk!7;mA^Rbi$J%$p+LxQjIj%S{W0@j*}W`_~4vgAj8p0ZD~+P%VKWn zwVzqhj-UsLfHYsll~Q|+wP7l zC@u!*vZ){Lw+!NYe->?e!zKJRMb+LHO{mJRJLkv8gRAAkbr*wP)XiqV7NM;lnliL8 z#Q9@)x$KHA^lvi$lm!=w6ZG)oRbX%ee* zrv={-YMoWi5}K(@d&LP8ne))JiS%BA21qIsMre|Zu-YRgSqrPAj+#zuXP{{kFSHRo zmJ6jNVMZF~qO~vEF-|1ChppfaoiiPGugLa8^Rd z5|l|JG}&cj&_`$+>)6wEskde%RA%uRo9OgHn3Q>606Fz(JK6Ofmf5 ztncrN=UtEK&c<8Pu#u*l{GYqH18@juV}H0EriLwsX6+gJnTel*+Ozs_Z<52lO2$p0 zF#xuf>YJ*nyOw{XU4v)1(p7OA2H5 z11v&YJQ392fWD|dS2swN(+w7U%=ol^fUVrizujeAVt#MAq8qkT#QEl@MOUor+g;no zCVXaCO_KGUL;tz6R$C5ycqjlpyc(h0U^qU396DzQlE&;q4(#k|`F+D|Un`@5zhkI` zdzx2VxT99r6svV;7}5L38OVLn)|>E>zgXS8>?uwcbq#)rzF4nxx%w7}3zVttil&R~ ND482}baeji;ya&5Aff;O literal 1497 zcmZvcZEw>s5Xay9Q{0sQ&g{5S^XTE-Q+`e)5m=&aCvW{vnMCf7`hMzG8~U=KA+PRH5! zO%LB#MiX3gG+T>t+3QI&s{}_GRM09AX3-~`46OwxxN;_%OkH5Zo-&Kp2F{E*vgvG+ zvWXLMkimEt6U_uG+0h8KJzQlNlyf!!8rDfW0@5bNl-I$v(VmhFk(2cx9K8)1?vgax zrewi%j2=?5I;Bzqfns)HM_96lQ{UP3tGu+_u@!vvEXZV-eIn@gF-8p+y``7hb>2sU zkOsSvt&mw~ATsSNAzFK56o`#MN1Pw2v}IHx$z-iI2D)vJ2NKZ;(fv zCLf~7(Yj!b(!|)@qeM2RHf$*hgHSScgxn$;hkCS+&A~$`*sEgC z#FKE+qD}@i9@DoLV;XjFC2&T9v3a9$WtO+C1{4_=au6dLQxEAQbkZA_w9d+%OspL% zVvnrYfTFJcr)LG3k8eaJK8xI5eeVp7mV0X3sFB0ie#qfo`@XHJx^3v;E7}=j^5yrB zk|uY>qU^!Pa=t7s>KA&*W$VUnqV0fov%%A}Z0hIDtZXP>7}MU}WnHypg&p%PFu=QF z14gb#xCMp~GTPlW@0W8(r5wSOhJ7yUXV~&9{SC;TC!D4|PdUJ5+;A88WziO^dU0s_ zvMC>hq9C^6=YR6f8ArhVK&`qNRU-<}na2*L+~@xw9Kb`wY>}ITRjkq818O(La#e0b zzI@+ZAsxp}y)IAacsW1WCS@|M>y~oBU4kd~%ReR5cb99iX}fS(2Pck>u0P%U1+f@H A?EnA( diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNUpload.imageset/Contents.json b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNUpload.imageset/Contents.json index 4efc61b832..c7fec6a1a8 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNUpload.imageset/Contents.json +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNUpload.imageset/Contents.json @@ -10,6 +10,6 @@ "version" : 1 }, "properties" : { - "template-rendering-intent" : "template" + "template-rendering-intent" : "original" } } diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNUpload.imageset/vpn-upload.pdf b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Resources/Assets.xcassets/Icons/VPNUpload.imageset/vpn-upload.pdf index 97a505ff336e46e603a8833b019ac9df300c5264..4cc3e7eced2500b611fc55263225057198362ad6 100644 GIT binary patch literal 1355 zcmZXUS#Q%o5P;w3SIkSL_Mz*&52>m|(?bXlA?2;&!8qGAD6zqIQQ_A!>-Y*;Mak~B zJNHb^mbdqpf;j+z9INj?0P*Syv%9zU;jzuzcsPmfr^t~r%hST^>%#|PxlUprkK{kb za?`$`BDkW5(6n{T_mJJj-`gUt9&T{)I6oB){DSOG;HClcr?bKFk`KFaGhKQnxV7G+ zpi3*Ym00Xye0aKPiV_?PGH}IFGZ&+P><1Gj6xvm*Eiq;GjpqLX*t1yF#Y4S{S zGSDgGI3vBuB-p>2xkgzft6WYY>?m8FOK$WOC}&z|?MLFM}eYm@B+_J!WT##-M8z|+o_EYvtgYbJ6?}8P0YKptl9>j zzR?8H%D(>j88KVto7jU-alOrNsyBS12>J#eH+?f7D&1Y;plB7F>a{LngK0!py|zVF zwy`9GY3>HN%xhw3dPH+KM3B%l-5<7V@)T15tI^J)dL>zL;(kM9rit=D)0Ec1a(GQSdPe%pU22^^5Q>4qkN169Z%3qS6k=X zU92G$@BJ;Mx)BDvQ<^Jm;&w+oIP&;M)JP` CDJHQ1 literal 1361 zcmZXUOHbQC5XbNPDdvI%2i80LUI`(U1W?shrJ*;}gX=6IRbsccsc1hvvv&L-tfFN7 z^UmYHGn13M<;^*A#~6Zu>gO*8aB%_I^-Ftu-{x)XpRoHWauD2#wBYix|FBrDlNpn5 z>_5kH(>{SBxT1rsY3rEpS++x7l{HQ|klae|LV(@GQ1B3BvY108$Hpoo$HFM(9gqyCf>9E5r^n7w!8DiTSkIy2 z!8#L2_vmdxW7L9;aF!l&3>Jl+avaBvx?koD^~(R9h~EHfRPrgo35&SdT6p zQ&cg!%L!@jB|$$~YHk!7;mA^Rbi$J%xecg;q&w1FYh_Frica3hhY!x_88RHL)Rvlc zuq>vMjuCdmw4h)n`RMZ)NuR6;@V*ra_QW1p$-c4F)`y-Q8a(xKKOiJ}Y64%@Os(OJ3QgG<>E!>U^51s zgfH?o-&LEprZ07TWC9%P<)?43#wY`X4~vT5?VubHwZRecG?vFmQI*mf4N(Zn^$7XN zeOu?-U94HE-p3_f$7xgT;~AW8*Ju5uWUH!bAqnm-IJ@8ekCc7)r Double { + static func dataVolume(colorScheme: ColorScheme) -> Double { colorScheme == .light ? Double(0.6) : Double(0.5) } static let content = Double(0.58) static let label = Double(0.9) - static let description = Double(0.9) static let link = Double(1) static func sectionHeader(colorScheme: ColorScheme) -> Double { @@ -93,6 +96,12 @@ fileprivate extension View { .foregroundColor(Color(.defaultText)) } + func applyDataVolumeAttributes(colorScheme: ColorScheme) -> some View { + opacity(Opacity.dataVolume(colorScheme: colorScheme)) + .font(.NetworkProtection.dataVolume) + .foregroundColor(Color(.defaultText)) + } + func applyLocationAttributes() -> some View { font(.NetworkProtection.location) } @@ -103,10 +112,9 @@ fileprivate extension View { .foregroundColor(Color(.defaultText)) } - func applyDescriptionAttributes(colorScheme: ColorScheme) -> some View { - opacity(Opacity.description) - .font(.NetworkProtection.description) - .foregroundColor(Color(.defaultText)) + func applyDescriptionAttributes() -> some View { + font(.NetworkProtection.description) + .foregroundColor(Color(.secondaryText)) } func applyLabelAttributes(colorScheme: ColorScheme) -> some View { @@ -190,7 +198,7 @@ public struct TunnelControllerView: View { Text(model.isToggleOn.wrappedValue ? UserText.networkProtectionStatusHeaderMessageOn : UserText.networkProtectionStatusHeaderMessageOff) .multilineText() .multilineTextAlignment(.center) - .applyDescriptionAttributes(colorScheme: colorScheme) + .applyDescriptionAttributes() .fixedSize(horizontal: false, vertical: true) .padding(EdgeInsets(top: 8, leading: 16, bottom: 16, trailing: 16)) } @@ -208,10 +216,6 @@ public struct TunnelControllerView: View { @ViewBuilder private func headerAnimationView(_ animationName: String) -> some View { LottieView(animation: .named(animationName)) - .configure { animationView in - animationView.contentMode = .scaleAspectFit - animationView.clipsToBounds = true - } .playing(withIntro: .init( skipIntro: model.isVPNEnabled && !model.isToggleDisabled, introStartFrame: 0, @@ -248,8 +252,21 @@ public struct TunnelControllerView: View { .background(Color(hex: "B2B2B2").opacity(0.3)) .clipShape(Circle()) } else if model.wantsNearestLocation { - Image(NetworkProtectionAsset.nearestAvailable) - .frame(width: 26, height: 26) + ZStack { + Circle() + .fill(Color(hex: "B2B2B2").opacity(0.3)) + .frame(width: 26, height: 26) + if isHovered { + Image(NetworkProtectionAsset.nearestAvailable) + .renderingMode(.template) + .foregroundColor(.white) + .frame(width: 16, height: 16) + } else { + Image(NetworkProtectionAsset.nearestAvailable) + .renderingMode(colorScheme == .light ? .original : .template) + .frame(width: 16, height: 16) + } + } } if #available(macOS 12, *) { if isHovered { @@ -257,7 +274,7 @@ public struct TunnelControllerView: View { .applyLocationAttributes() .foregroundColor(.white) } else { - Text(model.formattedLocation) + Text(model.formattedLocation(colorScheme: colorScheme)) .applyLocationAttributes() } } else { @@ -280,10 +297,11 @@ public struct TunnelControllerView: View { .applySectionHeaderAttributes(colorScheme: colorScheme) .padding(EdgeInsets(top: 6, leading: 9, bottom: 6, trailing: 9)) - connectionStatusRow(icon: .ipAddressIcon, - title: UserText.networkProtectionStatusViewIPAddress, + connectionStatusRow(title: UserText.networkProtectionStatusViewIPAddress, details: model.serverAddress) + dataVolumeRow(title: UserText.vpnDataVolume, dataVolume: model.formattedDataVolume) + dividerRow() } } @@ -322,11 +340,8 @@ public struct TunnelControllerView: View { .padding(EdgeInsets(top: 3, leading: 9, bottom: 3, trailing: 9)) } - private func connectionStatusRow(icon: NetworkProtectionAsset, title: String, details: String) -> some View { + private func connectionStatusRow(title: String, details: String) -> some View { HStack(spacing: 0) { - Image(icon) - .padding([.trailing], 8) - Text(title) .applyLabelAttributes(colorScheme: colorScheme) .fixedSize() @@ -338,6 +353,32 @@ public struct TunnelControllerView: View { .applyConnectionStatusDetailAttributes(colorScheme: colorScheme) .fixedSize() } + .padding(EdgeInsets(top: 6, leading: 10, bottom: 0, trailing: 9)) + } + + private func dataVolumeRow(title: String, dataVolume: TunnelControllerViewModel.FormattedDataVolume) -> some View { + HStack(spacing: 0) { + Text(title) + .applyLabelAttributes(colorScheme: colorScheme) + .fixedSize() + + Spacer(minLength: 2) + + Group { + Image(NetworkProtectionAsset.dataReceived) + .renderingMode(colorScheme == .light ? .original : .template) + .frame(width: 12, height: 12) + Text(dataVolume.dataReceived) + .applyDataVolumeAttributes(colorScheme: colorScheme) + Image(NetworkProtectionAsset.dataSent) + .renderingMode(colorScheme == .light ? .original : .template) + .frame(width: 12, height: 12) + .padding(.leading, 4) + Text(dataVolume.dataSent) + .applyDataVolumeAttributes(colorScheme: colorScheme) + } + .fixedSize() + } .padding(EdgeInsets(top: 6, leading: 10, bottom: 6, trailing: 9)) } } diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift index dfd96cd277..e28b2aed75 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerViewModel.swift @@ -23,6 +23,10 @@ import SwiftUI @MainActor public final class TunnelControllerViewModel: ObservableObject { + public struct FormattedDataVolume: Equatable { + public let dataSent: String + public let dataReceived: String + } /// The NetP service. /// @@ -56,6 +60,13 @@ public final class TunnelControllerViewModel: ObservableObject { private let locationFormatter: VPNLocationFormatting + private static let byteCountFormatter: ByteCountFormatter = { + let formatter = ByteCountFormatter() + formatter.allowsNonnumericFormatting = false + formatter.allowedUnits = [.useKB, .useMB, .useGB] + return formatter + }() + private let appLauncher: AppLaunching // MARK: - Misc @@ -70,6 +81,7 @@ public final class TunnelControllerViewModel: ObservableObject { private static let statusDispatchQueue = DispatchQueue(label: "com.duckduckgo.NetworkProtectionStatusView.statusDispatchQueue", qos: .userInteractive) private static let connectivityIssuesDispatchQueue = DispatchQueue(label: "com.duckduckgo.NetworkProtectionStatusView.connectivityIssuesDispatchQueue", qos: .userInteractive) private static let serverInfoDispatchQueue = DispatchQueue(label: "com.duckduckgo.NetworkProtectionStatusView.serverInfoDispatchQueue", qos: .userInteractive) + private static let dataVolumeDispatchQueue = DispatchQueue(label: "com.duckduckgo.NetworkProtectionStatusView.dataVolumeDispatchQueue", qos: .userInteractive) // MARK: - Initialization & Deinitialization @@ -90,6 +102,7 @@ public final class TunnelControllerViewModel: ObservableObject { self.appLauncher = appLauncher connectionStatus = statusReporter.statusObserver.recentValue + formattedDataVolume = statusReporter.dataVolumeObserver.recentValue.formatted(using: Self.byteCountFormatter) internalServerAddress = statusReporter.serverInfoObserver.recentValue.serverAddress internalServerAttributes = statusReporter.serverInfoObserver.recentValue.serverLocation internalServerLocation = internalServerAttributes?.serverLocation @@ -100,6 +113,7 @@ public final class TunnelControllerViewModel: ObservableObject { subscribeToOnboardingStatusChanges() subscribeToStatusChanges() subscribeToServerInfoChanges() + subscribeToDataVolumeUpdates() } deinit { @@ -156,6 +170,15 @@ public final class TunnelControllerViewModel: ObservableObject { .store(in: &cancellables) } + private func subscribeToDataVolumeUpdates() { + statusReporter.dataVolumeObserver.publisher + .subscribe(on: Self.dataVolumeDispatchQueue) + .map { $0.formatted(using: Self.byteCountFormatter) } + .receive(on: DispatchQueue.main) + .assign(to: \.formattedDataVolume, onWeaklyHeld: self) + .store(in: &cancellables) + } + // MARK: - ON/OFF Toggle private func startTimer() { @@ -444,6 +467,9 @@ public final class TunnelControllerViewModel: ObservableObject { @Published private var internalServerAttributes: NetworkProtectionServerInfo.ServerAttributes? + @Published + var formattedDataVolume: FormattedDataVolume + var wantsNearestLocation: Bool { guard case .nearest = vpnSettings.selectedLocation else { return false } return true @@ -460,11 +486,12 @@ public final class TunnelControllerViewModel: ObservableObject { } @available(macOS 12, *) - var formattedLocation: AttributedString { - locationFormatter.string(from: internalServerLocation, - preferredLocation: vpnSettings.selectedLocation, - locationTextColor: Color(.defaultText), - preferredLocationTextColor: Color(.defaultText).opacity(0.6)) + func formattedLocation(colorScheme: ColorScheme) -> AttributedString { + let opacity = colorScheme == .light ? Double(0.6) : Double(0.5) + return locationFormatter.string(from: internalServerLocation, + preferredLocation: vpnSettings.selectedLocation, + locationTextColor: Color(.defaultText), + preferredLocationTextColor: Color(.defaultText).opacity(opacity)) } // MARK: - Toggling VPN @@ -508,3 +535,10 @@ public final class TunnelControllerViewModel: ObservableObject { } } } + +extension DataVolume { + func formatted(using formatter: ByteCountFormatter) -> TunnelControllerViewModel.FormattedDataVolume { + .init(dataSent: formatter.string(fromByteCount: bytesSent), + dataReceived: formatter.string(fromByteCount: bytesReceived)) + } +} diff --git a/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionAssetTests.swift b/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionAssetTests.swift index 83d08e3ceb..b49d24d5b4 100644 --- a/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionAssetTests.swift +++ b/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/NetworkProtectionAssetTests.swift @@ -28,12 +28,12 @@ final class NetworkProtectionAssetTests: XCTestCase { /// func testAssetEnumValuesAreUnchanged() { let assetsAndExpectedRawValues: [NetworkProtectionAsset: String] = [ - .ipAddressIcon: "IP-16", - .serverLocationIcon: "Server-Location-16", .vpnDisabledImage: "VPNDisabled", .vpnEnabledImage: "VPN", .vpnIcon: "VPN-16", .nearestAvailable: "VPNLocation", + .dataReceived: "VPNDownload", + .dataSent: "VPNUpload", .appleVaultIcon: "apple-vault-icon", .appleVPNIcon: "apple-vpn-icon", .appleSystemSettingsIcon: "apple-system-settings-icon", diff --git a/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/TunnelControllerViewModelTests.swift b/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/TunnelControllerViewModelTests.swift index 884dc3d002..e7785da3f4 100644 --- a/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/TunnelControllerViewModelTests.swift +++ b/LocalPackages/NetworkProtectionMac/Tests/NetworkProtectionUITests/TunnelControllerViewModelTests.swift @@ -36,12 +36,14 @@ final class TunnelControllerViewModelTests: XCTestCase { let connectionErrorObserver: ConnectionErrorObserver let connectivityIssuesObserver: ConnectivityIssueObserver let controllerErrorMessageObserver: ControllerErrorMesssageObserver + let dataVolumeObserver: DataVolumeObserver init(status: ConnectionStatus, isHavingConnectivityIssues: Bool = false, serverInfo: NetworkProtectionStatusServerInfo = MockStatusReporter.defaultServerInfo, tunnelErrorMessage: String? = nil, - controllerErrorMessage: String? = nil) { + controllerErrorMessage: String? = nil, + dataVolume: DataVolume = .init()) { let mockStatusObserver = MockConnectionStatusObserver() mockStatusObserver.subject.send(status) @@ -62,6 +64,10 @@ final class TunnelControllerViewModelTests: XCTestCase { let mockControllerErrorMessageObserver = MockControllerErrorMesssageObserver() mockControllerErrorMessageObserver.subject.send(controllerErrorMessage) controllerErrorMessageObserver = mockControllerErrorMessageObserver + + let mockDataVolumeObserver = MockDataVolumeObserver() + mockDataVolumeObserver.subject.send(dataVolume) + dataVolumeObserver = mockDataVolumeObserver } func forceRefresh() { @@ -189,6 +195,24 @@ final class TunnelControllerViewModelTests: XCTestCase { XCTAssertFalse(model.showServerDetails) } + /// We expect the model to properly reflect the data volume. + /// + @MainActor + func testProperlyReflectsDataVolume() async throws { + let controller = MockTunnelController() + let statusReporter = MockStatusReporter(status: .connected(connectedDate: Date()), + dataVolume: .init(bytesSent: 512000, bytesReceived: 1024000)) + let model = TunnelControllerViewModel( + controller: controller, + onboardingStatusPublisher: Just(OnboardingStatus.completed).eraseToAnyPublisher(), + statusReporter: statusReporter, + vpnSettings: .init(defaults: .standard), + locationFormatter: MockVPNLocationFormatter(), + appLauncher: MockAppLauncher()) + + XCTAssertEqual(model.formattedDataVolume, .init(dataSent: "512 KB", dataReceived: "1 MB")) + } + /// We expect that setting the model's `isRunning` to `true`, will start the VPN. /// @MainActor diff --git a/UnitTests/NetworkProtection/DefaultVPNLocationFormatterTests.swift b/UnitTests/NetworkProtection/DefaultVPNLocationFormatterTests.swift index 0c52440181..56190708be 100644 --- a/UnitTests/NetworkProtection/DefaultVPNLocationFormatterTests.swift +++ b/UnitTests/NetworkProtection/DefaultVPNLocationFormatterTests.swift @@ -39,7 +39,7 @@ final class DefaultVPNLocationFormatterTests: XCTestCase { XCTAssertEqual(formatter.emoji(for: server.country, preferredLocation: preferredLocation), "🇺🇸") XCTAssertEqual(formatter.emoji(for: server.country, preferredLocation: otherPreferredLocation), "🇺🇸") - XCTAssertEqual(formatter.string(from: nil, preferredLocation: .nearest), "Nearest available") + XCTAssertEqual(formatter.string(from: nil, preferredLocation: .nearest), "Nearest Location") XCTAssertEqual(formatter.string(from: nil, preferredLocation: preferredLocation), "United States") XCTAssertEqual(formatter.string(from: nil, preferredLocation: otherPreferredLocation), "United Kingdom") XCTAssertEqual(formatter.string(from: server.serverLocation, preferredLocation: .nearest), "Lafayette, United States (Nearest)") @@ -69,7 +69,7 @@ final class DefaultVPNLocationFormatterTests: XCTestCase { XCTAssertEqual(formatter.emoji(for: server.country, preferredLocation: preferredLocation), "🇨🇦") XCTAssertEqual(formatter.emoji(for: server.country, preferredLocation: otherPreferredLocation), "🇨🇦") - XCTAssertEqual(formatter.string(from: nil, preferredLocation: .nearest), "Nearest available") + XCTAssertEqual(formatter.string(from: nil, preferredLocation: .nearest), "Nearest Location") XCTAssertEqual(formatter.string(from: nil, preferredLocation: preferredLocation), "Canada") XCTAssertEqual(formatter.string(from: nil, preferredLocation: otherPreferredLocation), "United Kingdom") XCTAssertEqual(formatter.string(from: server.serverLocation, preferredLocation: .nearest), "Toronto, Canada (Nearest)") From 36576f70deaa0550390a8ddf629984c7b834a0e5 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Fri, 19 Apr 2024 10:16:55 -0400 Subject: [PATCH 17/22] Update BSK --- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index dcc3605227..c2881cd876 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -33,7 +33,7 @@ "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { "branch" : "anh/netp/screen-improvements", - "revision" : "3b354a70cdfff5d46d2c8f525748dee63a36ad88" + "revision" : "b49da84d19d5e9931341a5ad35b8e17829bafb91" } }, { From 6cf6f5d3e964495db098ef3ac79687538e97d2b2 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Fri, 19 Apr 2024 10:48:03 -0400 Subject: [PATCH 18/22] Fix Swiftlint warnings --- DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift b/DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift index dc34fd3b52..b9d7de926e 100644 --- a/DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift +++ b/DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift @@ -222,7 +222,7 @@ extension HomePage.Models { refreshFeaturesMatrix() } - // swiftlint:disable:next cyclomatic_complexity function_body_length + // swiftlint:disable:next cyclomatic_complexity func refreshFeaturesMatrix() { var features: [FeatureType] = [] #if DBP From e05cc7c02555a4e93a8e6da63f0114fbc184906d Mon Sep 17 00:00:00 2001 From: Anh Do Date: Fri, 19 Apr 2024 11:06:16 -0400 Subject: [PATCH 19/22] Fix botched merge --- DuckDuckGo.xcodeproj/project.pbxproj | 33 +++++++++++++++++----------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 167e7eaf3d..59c7502f13 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -2411,12 +2411,8 @@ BD384ACA2BBC821A00EF3735 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; BD384ACB2BBC821B00EF3735 /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; BD384ACC2BBC821B00EF3735 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; - BD384ACD2BBC821D00EF3735 /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; - BD384ACE2BBC821D00EF3735 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; - BD8A305D2BC425B000D0669F /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = BD8A305C2BC425B000D0669F /* Lottie */; }; BDA7647C2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA7647B2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift */; }; BDA7647D2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA7647B2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift */; }; - BDA7647E2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA7647B2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift */; }; BDA7647F2BC4998900D0400C /* DefaultVPNLocationFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA7647B2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift */; }; BDA764802BC4998A00D0400C /* DefaultVPNLocationFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA7647B2BC497BE00D0400C /* DefaultVPNLocationFormatter.swift */; }; BDA764842BC49E3F00D0400C /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */; }; @@ -2425,10 +2421,12 @@ BDA7648E2BC4E4EF00D0400C /* DefaultVPNLocationFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA7648C2BC4E4EF00D0400C /* DefaultVPNLocationFormatterTests.swift */; }; BDA764912BC4E57200D0400C /* MockVPNLocationFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA764902BC4E57200D0400C /* MockVPNLocationFormatter.swift */; }; BDA764922BC4E57200D0400C /* MockVPNLocationFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA764902BC4E57200D0400C /* MockVPNLocationFormatter.swift */; }; - BDE981D82BBCE4C700645880 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = BDE981D72BBCE4C700645880 /* Lottie */; }; + BDADBDC92BD2BC2200421B9B /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = BDADBDC82BD2BC2200421B9B /* Lottie */; }; + BDADBDCB2BD2BC2800421B9B /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = BDADBDCA2BD2BC2800421B9B /* Lottie */; }; + BDADBDCC2BD2BC4D00421B9B /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; + BDADBDCD2BD2BC5700421B9B /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; BDE981D92BBD10D600645880 /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; BDE981DA2BBD10D600645880 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; - BDE981DF2BBDBD0100645880 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = BDE981DE2BBDBD0100645880 /* Lottie */; }; C1372EF42BBC5BAD003F8793 /* SecureTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1372EF32BBC5BAD003F8793 /* SecureTextField.swift */; }; C1372EF52BBC5BAD003F8793 /* SecureTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1372EF32BBC5BAD003F8793 /* SecureTextField.swift */; }; C13909EF2B85FD4E001626ED /* AutofillActionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13909EE2B85FD4E001626ED /* AutofillActionExecutor.swift */; }; @@ -4087,7 +4085,7 @@ 7BEEA5122AD1235B00A9E72B /* NetworkProtectionIPC in Frameworks */, 7BA7CC5F2AD1210C0042E5CE /* Networking in Frameworks */, 7BEEA5162AD1236E00A9E72B /* NetworkProtectionUI in Frameworks */, - BDE981D82BBCE4C700645880 /* Lottie in Frameworks */, + BDADBDC92BD2BC2200421B9B /* Lottie in Frameworks */, 7BFCB74E2ADE7E1A00DA3EA7 /* PixelKit in Frameworks */, EE7295ED2A545C0A008C0991 /* NetworkProtection in Frameworks */, EE2F9C5B2B90F2FF00D45FC9 /* Subscription in Frameworks */, @@ -4101,9 +4099,9 @@ files = ( 7BFCB7502ADE7E2300DA3EA7 /* PixelKit in Frameworks */, 4BCBE45C2BA7E18500FC75A1 /* Subscription in Frameworks */, - BDE981DF2BBDBD0100645880 /* Lottie in Frameworks */, 7BA7CC612AD1211C0042E5CE /* Networking in Frameworks */, 7BEEA5142AD1236300A9E72B /* NetworkProtectionIPC in Frameworks */, + BDADBDCB2BD2BC2800421B9B /* Lottie in Frameworks */, 7B00997F2B6508C200FE7C31 /* NetworkProtectionProxy in Frameworks */, EE7295EF2A545C12008C0991 /* NetworkProtection in Frameworks */, 4B2D067F2A1334D700DE1F49 /* NetworkProtectionUI in Frameworks */, @@ -4117,7 +4115,6 @@ files = ( 7B624F172BA25C1F00A6C544 /* NetworkProtectionUI in Frameworks */, 37269F052B3332C2005E8E46 /* Common in Frameworks */, - BD8A305D2BC425B000D0669F /* Lottie in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -5011,7 +5008,6 @@ children = ( 4B4D60722A0B29FA00BCD287 /* EventMapping+NetworkProtectionError.swift */, BDE981DB2BBD110800645880 /* Assets */, - EEA3EEAF2B24EB5100E8333A /* VPNLocation */, 4B4D606B2A0B29FA00BCD287 /* Invite */, 7BD3AF5C2A8E7AF1006F9F56 /* KeychainType+ClientDefault.swift */, 9D9AE8682AA76CDC0026E7DC /* LoginItem+NetworkProtection.swift */, @@ -8262,7 +8258,7 @@ 4B41EDAA2B1544B2001EEDF4 /* LoginItems */, 7B00997C2B6508B700FE7C31 /* NetworkProtectionProxy */, EE2F9C5A2B90F2FF00D45FC9 /* Subscription */, - BDE981D72BBCE4C700645880 /* Lottie */, + BDADBDC82BD2BC2200421B9B /* Lottie */, ); productName = DuckDuckGoAgent; productReference = 4B2D06392A11CFBB00DE1F49 /* DuckDuckGo VPN.app */; @@ -8295,7 +8291,7 @@ 7B00997E2B6508C200FE7C31 /* NetworkProtectionProxy */, 4BA7C4DC2B3F64E500AFE511 /* LoginItems */, 4BCBE45B2BA7E18500FC75A1 /* Subscription */, - BDE981DE2BBDBD0100645880 /* Lottie */, + BDADBDCA2BD2BC2800421B9B /* Lottie */, ); productName = DuckDuckGoAgentAppStore; productReference = 4B2D06692A13318400DE1F49 /* DuckDuckGo VPN App Store.app */; @@ -8318,7 +8314,6 @@ packageProductDependencies = ( 37269F042B3332C2005E8E46 /* Common */, 7B624F162BA25C1F00A6C544 /* NetworkProtectionUI */, - BD8A305C2BC425B000D0669F /* Lottie */, ); productName = DuckDuckGoNotifications; productReference = 4B4BEC202A11B4E2001D9AC5 /* DuckDuckGo Notifications.app */; @@ -8828,7 +8823,9 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + BDADBDCD2BD2BC5700421B9B /* vpn-light-mode.json in Resources */, 7BA7CC472AD11E5C0042E5CE /* Assets.xcassets in Resources */, + BDADBDCC2BD2BC4D00421B9B /* vpn-dark-mode.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -13228,6 +13225,16 @@ package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Configuration; }; + BDADBDC82BD2BC2200421B9B /* Lottie */ = { + isa = XCSwiftPackageProductDependency; + package = 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */; + productName = Lottie; + }; + BDADBDCA2BD2BC2800421B9B /* Lottie */ = { + isa = XCSwiftPackageProductDependency; + package = 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */; + productName = Lottie; + }; CBC83E3529B63D380008E19C /* Configuration */ = { isa = XCSwiftPackageProductDependency; package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; From bb90b30993e1f3d79aa23570db11ad105e5c8e47 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Fri, 19 Apr 2024 11:27:05 -0400 Subject: [PATCH 20/22] Fix broken tests --- .../Support/NetworkProtectionTestingSupport.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/UnitTests/NetworkProtection/Support/NetworkProtectionTestingSupport.swift b/UnitTests/NetworkProtection/Support/NetworkProtectionTestingSupport.swift index e298ad553d..4e6de202e7 100644 --- a/UnitTests/NetworkProtection/Support/NetworkProtectionTestingSupport.swift +++ b/UnitTests/NetworkProtection/Support/NetworkProtectionTestingSupport.swift @@ -101,13 +101,19 @@ struct MockConnectionErrorObserver: ConnectionErrorObserver { var recentValue: String? } -struct MockIPCClient: NetworkProtectionIPCClient { +struct MockDataVolumeObserver: DataVolumeObserver { + var publisher: AnyPublisher = Just(.init()).eraseToAnyPublisher() + + var recentValue: DataVolume = .init() +} +struct MockIPCClient: NetworkProtectionIPCClient { private let error: Error? var ipcStatusObserver: NetworkProtection.ConnectionStatusObserver = MockConnectionStatusObserver() var ipcServerInfoObserver: NetworkProtection.ConnectionServerInfoObserver = MockServerInfoObserver() var ipcConnectionErrorObserver: NetworkProtection.ConnectionErrorObserver = MockConnectionErrorObserver() + var ipcDataVolumeObserver: any NetworkProtection.DataVolumeObserver = MockDataVolumeObserver() init(error: Error? = nil) { self.error = error From ea5735287c2fb600f44909d7333a5ea8909d5360 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Fri, 19 Apr 2024 11:54:14 -0400 Subject: [PATCH 21/22] Fix tests --- .../NetworkProtection/DefaultVPNLocationFormatterTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UnitTests/NetworkProtection/DefaultVPNLocationFormatterTests.swift b/UnitTests/NetworkProtection/DefaultVPNLocationFormatterTests.swift index 56190708be..e9abed7230 100644 --- a/UnitTests/NetworkProtection/DefaultVPNLocationFormatterTests.swift +++ b/UnitTests/NetworkProtection/DefaultVPNLocationFormatterTests.swift @@ -29,7 +29,7 @@ final class DefaultVPNLocationFormatterTests: XCTestCase { } func testUSLocation() { - let server = NetworkProtectionServerInfo.ServerAttributes(city: "Lafayette", country: "us", state: "la", timezoneOffset: 0) + let server = NetworkProtectionServerInfo.ServerAttributes(city: "Lafayette", country: "us", state: "la") let preferredLocation = VPNSettings.SelectedLocation.location(.init(country: "us")) let otherPreferredLocation = VPNSettings.SelectedLocation.location(.init(country: "gb")) @@ -59,7 +59,7 @@ final class DefaultVPNLocationFormatterTests: XCTestCase { } func testCALocation() { - let server = NetworkProtectionServerInfo.ServerAttributes(city: "Toronto", country: "ca", state: "on", timezoneOffset: 0) + let server = NetworkProtectionServerInfo.ServerAttributes(city: "Toronto", country: "ca", state: "on") let preferredLocation = VPNSettings.SelectedLocation.location(.init(country: "ca")) let otherPreferredLocation = VPNSettings.SelectedLocation.location(.init(country: "gb")) From 1e39ac12649e908db7d4993da308c116b31c05b6 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Fri, 19 Apr 2024 12:11:11 -0400 Subject: [PATCH 22/22] Update BSK --- DuckDuckGo.xcodeproj/project.pbxproj | 4 ++-- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- LocalPackages/DataBrokerProtection/Package.swift | 2 +- LocalPackages/NetworkProtectionMac/Package.swift | 2 +- LocalPackages/SubscriptionUI/Package.swift | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 2097b1db78..06e4ce3ba6 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -12645,8 +12645,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { - branch = "anh/netp/screen-improvements"; - kind = branch; + kind = exactVersion; + version = 138.0.0; }; }; 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 34d8fc133d..462a0cc553 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { - "branch" : "anh/netp/screen-improvements", - "revision" : "b49da84d19d5e9931341a5ad35b8e17829bafb91" + "revision" : "b8f0e5db431c63943b509d522c157f870ef03ae0", + "version" : "138.0.0" } }, { diff --git a/LocalPackages/DataBrokerProtection/Package.swift b/LocalPackages/DataBrokerProtection/Package.swift index df2e1a5447..51d8ee4755 100644 --- a/LocalPackages/DataBrokerProtection/Package.swift +++ b/LocalPackages/DataBrokerProtection/Package.swift @@ -29,7 +29,7 @@ let package = Package( targets: ["DataBrokerProtection"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "137.0.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "138.0.0"), .package(path: "../PixelKit"), .package(path: "../SwiftUIExtensions"), .package(path: "../XPCHelper"), diff --git a/LocalPackages/NetworkProtectionMac/Package.swift b/LocalPackages/NetworkProtectionMac/Package.swift index 17669b5f8a..c08127a1af 100644 --- a/LocalPackages/NetworkProtectionMac/Package.swift +++ b/LocalPackages/NetworkProtectionMac/Package.swift @@ -31,7 +31,7 @@ let package = Package( .library(name: "NetworkProtectionUI", targets: ["NetworkProtectionUI"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "137.0.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "138.0.0"), .package(path: "../XPCHelper"), .package(path: "../SwiftUIExtensions"), .package(path: "../LoginItems"), diff --git a/LocalPackages/SubscriptionUI/Package.swift b/LocalPackages/SubscriptionUI/Package.swift index 524e9ef352..9092bb8c50 100644 --- a/LocalPackages/SubscriptionUI/Package.swift +++ b/LocalPackages/SubscriptionUI/Package.swift @@ -12,7 +12,7 @@ let package = Package( targets: ["SubscriptionUI"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "137.0.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "138.0.0"), .package(path: "../SwiftUIExtensions") ], targets: [