Skip to content

Commit

Permalink
Merge branch 'main' into twn
Browse files Browse the repository at this point in the history
  • Loading branch information
tonisevener authored Oct 10, 2024
2 parents ef3013a + 1ba143c commit 532f66f
Show file tree
Hide file tree
Showing 21 changed files with 380 additions and 364 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import WMFData
let enableAltTextExperimentForEN: String
let alwaysShowAltTextEntryPoint: String
let sendAnalyticsToWMFLabs: String
let enableYearinReview: String
let close: String

@objc public init(developerSettings: String, doNotPostImageRecommendations: String, enableAltTextExperimentForEN: String, alwaysShowAltTextEntryPoint: String, sendAnalyticsToWMFLabs: String, close: String) {
@objc public init(developerSettings: String, doNotPostImageRecommendations: String, enableAltTextExperimentForEN: String, alwaysShowAltTextEntryPoint: String, sendAnalyticsToWMFLabs: String, enableYearinReview: String, close: String) {
self.developerSettings = developerSettings
self.doNotPostImageRecommendations = doNotPostImageRecommendations
self.enableAltTextExperimentForEN = enableAltTextExperimentForEN
self.alwaysShowAltTextEntryPoint = alwaysShowAltTextEntryPoint
self.sendAnalyticsToWMFLabs = sendAnalyticsToWMFLabs
self.enableYearinReview = enableYearinReview
self.close = close
}
}
Expand All @@ -33,13 +35,12 @@ import WMFData
let enableAltTextExperimentItemForENItem = WMFFormItemSelectViewModel(title: localizedStrings.enableAltTextExperimentForEN, isSelected: WMFDeveloperSettingsDataController.shared.enableAltTextExperimentForEN)
let alwaysShowAltTextEntryPointItem = WMFFormItemSelectViewModel(title: localizedStrings.alwaysShowAltTextEntryPoint, isSelected: WMFDeveloperSettingsDataController.shared.alwaysShowAltTextEntryPoint)
let sendAnalyticsToWMFLabsItem = WMFFormItemSelectViewModel(title: localizedStrings.sendAnalyticsToWMFLabs, isSelected: WMFDeveloperSettingsDataController.shared.sendAnalyticsToWMFLabs)
let enableYearinReviewItem = WMFFormItemSelectViewModel(title: localizedStrings.enableYearinReview, isSelected: WMFDeveloperSettingsDataController.shared.enableYearInReview)

formViewModel = WMFFormViewModel(sections: [WMFFormSectionSelectViewModel(items: [doNotPostImageRecommendationsEditItem, enableAltTextExperimentItemForENItem, alwaysShowAltTextEntryPointItem, sendAnalyticsToWMFLabsItem], selectType: .multi)])
formViewModel = WMFFormViewModel(sections: [WMFFormSectionSelectViewModel(items: [doNotPostImageRecommendationsEditItem, enableAltTextExperimentItemForENItem, alwaysShowAltTextEntryPointItem, sendAnalyticsToWMFLabsItem, enableYearinReviewItem], selectType: .multi)])

doNotPostImageRecommendationsEditItem.$isSelected.sink { isSelected in

WMFDeveloperSettingsDataController.shared.doNotPostImageRecommendationsEdit = isSelected

}.store(in: &subscribers)

enableAltTextExperimentItemForENItem.$isSelected.sink { isSelected in
Expand All @@ -54,6 +55,10 @@ import WMFData
WMFDeveloperSettingsDataController.shared.sendAnalyticsToWMFLabs = isSelected
}.store(in: &subscribers)

enableYearinReviewItem.$isSelected.sink { isSelected in
WMFDeveloperSettingsDataController.shared.enableYearInReview = isSelected
}.store(in: &subscribers)

}

}
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
import Foundation

@objc public final class WMFDeveloperSettingsDataController: NSObject {

@objc public static let shared = WMFDeveloperSettingsDataController()

private let service: WMFService?
private let sharedCacheStore: WMFKeyValueStore?

private var featureConfig: WMFFeatureConfigResponse?

private let cacheDirectoryName = WMFSharedCacheDirectoryNames.developerSettings.rawValue
private let cacheFeatureConfigFileName = "AppsFeatureConfig"

public init(service: WMFService? = WMFDataEnvironment.current.basicService, sharedCacheStore: WMFKeyValueStore? = WMFDataEnvironment.current.sharedCacheStore) {
self.service = service
self.sharedCacheStore = sharedCacheStore
}

// MARK: - Local Settings from App Settings Menu

private let userDefaultsStore = WMFDataEnvironment.current.userDefaultsStore

Expand Down Expand Up @@ -37,4 +52,81 @@ import Foundation
try? userDefaultsStore?.save(key: WMFUserDefaultsKey.developerSettingsSendAnalyticsToWMFLabs.rawValue, value: newValue)
}
}

public var enableYearInReview: Bool {
get {
return ( try? userDefaultsStore?.load(key: WMFUserDefaultsKey.yearInReviewEnabled.rawValue)) ?? false
} set {
try? userDefaultsStore?.save(key: WMFUserDefaultsKey.yearInReviewEnabled.rawValue, value: newValue)
}
}

// MARK: - Remote Settings from donatewiki AppsFeatureConfig json

public func loadFeatureConfig() -> WMFFeatureConfigResponse? {

// First pull from memory
guard featureConfig == nil else {
return featureConfig
}

// Fall back to persisted objects if within four hours
let featureConfig: WMFFeatureConfigResponse? = try? sharedCacheStore?.load(key: cacheDirectoryName, cacheFeatureConfigFileName)

guard let featureConfigCachedDate = featureConfig?.cachedDate else {
return nil
}

let fourHours = TimeInterval(60 * 60 * 4)
guard (-featureConfigCachedDate.timeIntervalSinceNow) < fourHours else {
return nil
}

self.featureConfig = featureConfig

return featureConfig
}

@objc public func fetchFeatureConfig(completion: @escaping (Error?) -> Void) {

guard let service else {
completion(WMFDataControllerError.basicServiceUnavailable)
return
}

guard let featureConfigURL = URL.featureConfigURL() else {
completion(WMFDataControllerError.basicServiceUnavailable)
return
}

let featureConfigParameters: [String: Any] = [
"action": "raw"
]

let featureConfigRequest = WMFBasicServiceRequest(url: featureConfigURL, method: .GET, parameters: featureConfigParameters, acceptType: .json)
service.performDecodableGET(request: featureConfigRequest) { [weak self] (result: Result<WMFFeatureConfigResponse, Error>) in

guard let self else {
return
}

switch result {
case .success(let response):
self.featureConfig = response
self.featureConfig?.cachedDate = Date()

do {
try self.sharedCacheStore?.save(key: self.cacheDirectoryName, self.cacheFeatureConfigFileName, value: featureConfig)
} catch {
print(error)
}


completion(nil)
case .failure(let error):
completion(error)
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -424,21 +424,21 @@ private struct WMFFundraisingCampaignConfigResponse: Codable {
wmfVersion = try versionContainer.decode(WMFConfigVersion.self)
} catch {
// Skip
_ = try? versionContainer.decode(DiscardedElement.self)
_ = try? campaignContainer.decode(DiscardedElement.self)
_ = try? versionContainer.decode(WMFDiscardedElement.self)
_ = try? campaignContainer.decode(WMFDiscardedElement.self)
continue
}

guard wmfVersion.version == Self.currentVersion else {
_ = try? campaignContainer.decode(DiscardedElement.self)
_ = try? campaignContainer.decode(WMFDiscardedElement.self)
continue
}

do {
config = try campaignContainer.decode(FundraisingCampaignConfig.self)
} catch {
// Skip
_ = try? campaignContainer.decode(DiscardedElement.self)
_ = try? campaignContainer.decode(WMFDiscardedElement.self)
continue
}

Expand All @@ -452,8 +452,6 @@ private struct WMFFundraisingCampaignConfigResponse: Codable {
var container = encoder.unkeyedContainer()
try container.encode(contentsOf: configs)
}

struct DiscardedElement: Codable {}
}

private struct WMFFundraisingCampaignPromptState: Codable {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

final public class WMFYearInReviewDataController {

}
15 changes: 15 additions & 0 deletions WMFData/Sources/WMFData/Extensions/URL+API.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,19 @@ extension URL {
}
return components.url
}

static func featureConfigURL(environment: WMFServiceEnvironment = WMFDataEnvironment.current.serviceEnvironment) -> URL? {

var components = URLComponents()
components.scheme = "https"
components.path = "/wiki/MediaWiki:AppsFeatureConfig.json"

switch environment {
case .production:
components.host = "donate.wikimedia.org"
case .staging:
components.host = "test.wikipedia.org"
}
return components.url
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import Foundation

public struct WMFFeatureConfigResponse: Codable {
public struct FeatureConfigIOS: Codable {

public struct YearInReview: Codable {

public struct PersonalizedSlides: Codable {
let readCount: SlideSettings
let editCount: SlideSettings
}

public struct SlideSettings: Codable {
public let isEnabled: Bool
}

public let isEnabled: Bool
public let countryCodes: [String]
public let primaryAppLanguageCodes: [String]
public let dataPopulationStartDateString: String
public let dataPopulationEndDateString: String
public let personalizedSlides: PersonalizedSlides
}

let version: Int
public let yir: YearInReview
}

public let ios: [FeatureConfigIOS]
var cachedDate: Date?

private let currentFeatureConfigVersion = 1

public init(from decoder: Decoder) throws {

// Custom decoding to filter out invalid versions

let overallContainer = try decoder.container(keyedBy: CodingKeys.self)

var versionContainer = try overallContainer.nestedUnkeyedContainer(forKey: .ios)
var iosContainer = try overallContainer.nestedUnkeyedContainer(forKey: .ios)

var validConfigs: [FeatureConfigIOS] = []
while !versionContainer.isAtEnd {

let wmfVersion: WMFConfigVersion
let config: FeatureConfigIOS
do {
wmfVersion = try versionContainer.decode(WMFConfigVersion.self)
} catch {
// Skip
_ = try? versionContainer.decode(WMFDiscardedElement.self)
_ = try? iosContainer.decode(WMFDiscardedElement.self)
continue
}

guard wmfVersion.version == currentFeatureConfigVersion else {
// Skip
_ = try? iosContainer.decode(WMFDiscardedElement.self)
continue
}

do {
config = try iosContainer.decode(FeatureConfigIOS.self)
} catch {
// Skip
_ = try? iosContainer.decode(WMFDiscardedElement.self)
continue
}

validConfigs.append(config)
}

self.ios = validConfigs
self.cachedDate = try? overallContainer.decode(Date.self, forKey: .cachedDate)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
struct WMFDiscardedElement: Codable {}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ enum WMFSharedCacheDirectoryNames: String {
case donorExperience = "Donor Experience"
case watchlists = "Watchlists"
case experiments = "Experiments"
case developerSettings = "Developer Settings"
}
1 change: 1 addition & 0 deletions WMFData/Sources/WMFData/Store/WMFUserDefaultsKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ enum WMFUserDefaultsKey: String {
case sawAltTextArticleEditorPrompt = "saw-alt-text-article-editor-prompt"
case altTextExperimentOnboarding = "alt-text-experiment-onboarding"
case hasLocallySavedDonations = "donate-history-has-locally-saved-donations"
case yearInReviewEnabled = "year-in-review-enabled"
}
30 changes: 30 additions & 0 deletions WMFData/Sources/WMFDataMocks/Resources/feature-get-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"common": {},
"ios": [
{
"version": 1,
"yir": {
"isEnabled": true,
"countryCodes": [
"FR",
"IT"
],
"primaryAppLanguageCodes": [
"fr",
"it"
],
"dataPopulationStartDateString": "2024-01-01T00:00:00Z",
"dataPopulationEndDateString": "2024-11-01T00:00:00Z",
"personalizedSlides": {
"readCount": {
"isEnabled": true
},
"editCount": {
"isEnabled": true
}
}
}
}
],
"android": {}
}
33 changes: 33 additions & 0 deletions WMFData/Sources/WMFDataMocks/WMFMockBasicService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,30 @@ internal enum WMFMockError: Error {
}

fileprivate extension WMFData.WMFServiceRequest {

var isFeatureConfigGet: Bool {
switch WMFDataEnvironment.current.serviceEnvironment {
case .production:
guard let url,
url.host == "donate.wikimedia.org",
url.path == "/wiki/MediaWiki:AppsFeatureConfig.json",
let action = parameters?["action"] as? String else {
return false
}

return method == .GET && action == "raw"
case .staging:
guard let url,
url.host == "test.wikipedia.org",
url.path == "/wiki/MediaWiki:AppsFeatureConfig.json",
let action = parameters?["action"] as? String else {
return false
}

return method == .GET && action == "raw"
}
}

var isDonateConfigGet: Bool {
switch WMFDataEnvironment.current.serviceEnvironment {
case .production:
Expand Down Expand Up @@ -158,6 +182,15 @@ public class WMFMockBasicService: WMFService {
return nil
}

return jsonData
} else if request.isFeatureConfigGet {
let resourceName = "feature-get-config"

guard let url = Bundle.module.url(forResource: resourceName, withExtension: "json"),
let jsonData = try? Data(contentsOf: url) else {
return nil
}

return jsonData
} else if request.isPaymentMethodsGet {
let resourceName = "donate-get-payment-methods"
Expand Down
Loading

0 comments on commit 532f66f

Please sign in to comment.