Skip to content

Commit 7ed8c6a

Browse files
committed
Merge branch 'main' into T410641_edit-impact-data
2 parents 3af6dc9 + b7a2e77 commit 7ed8c6a

File tree

146 files changed

+793
-477
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

146 files changed

+793
-477
lines changed

WMF Framework/CommonStrings.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ public class CommonStrings: NSObject {
197197
@objc public static let termsOfUseURLString = "https://foundation.wikimedia.org/wiki/Policy:Terms_of_Use"
198198

199199
@objc public static let account = WMFLocalizedString("settings-account", value: "Account", comment: "Title for button and page letting user view their account page.")
200+
@objc public static let logIn = WMFLocalizedString("main-menu-account-login", value: "Log in", comment: "Button text for logging in.")
200201

201202
@objc public static let myLanguages = WMFLocalizedString("settings-my-languages", value: "My languages", comment: "Title for list of user's preferred languages")
202203
@objc public static let readingPreferences = WMFLocalizedString("settings-appearance", value: "App theme", comment: "Title of the app theme screen.")
@@ -249,6 +250,7 @@ public class CommonStrings: NSObject {
249250
@objc public static let turnOnExploreTabTitle = WMFLocalizedString("explore-feed-preferences-turn-on-explore-tab-title", value: "Turn on the Explore tab?", comment: "Title for alert that allows users to turn on the Explore tab")
250251
@objc public static let turnOnExploreActionTitle = WMFLocalizedString("explore-feed-preferences-turn-on-explore-tab-action-title", value: "Turn on Explore", comment: "Title for action that allows users to turn on the Explore tab")
251252
@objc public static let customizeExploreFeedTitle = WMFLocalizedString("explore-feed-preferences-customize-explore-feed-action-title", value: "Customize Explore feed", comment: "Title for action that allows users to go to the Explore feed settings screen")
253+
public static let customize = WMFLocalizedString("customize-action-title", value: "Customize", comment: "Title for action that allows users to customize their preferences.")
252254

253255
@objc public static let revertedEditTitle = WMFLocalizedString("reverted-edit-title", value: "Reverted edit", comment: "Title for notification informing user that their edit was reverted.")
254256

@@ -269,8 +271,6 @@ public class CommonStrings: NSObject {
269271

270272
@objc public static let editAttribution = WMFLocalizedString("wikitext-upload-save-anonymously-warning", value: "Edits will be attributed to the IP address of your device. If you %1$@ you will have more privacy.", comment: "Button sub-text informing user or draw-backs of not signing in before saving wikitext. Parameters:\n* %1$@ - sign in button text")
271273

272-
@objc public static let editSignIn = WMFLocalizedString("wikitext-upload-save-sign-in", value: "Log in", comment: "{{Identical|Log in}}")
273-
274274
public static let genericErrorDescription = WMFLocalizedString("fetcher-error-generic", value: "Something went wrong. Please try again later.", comment: "Error shown to the user for generic errors with no clear recovery steps for the user.")
275275

276276
public static let insertMediaTitle = WMFLocalizedString("insert-media-title", value: "Insert media", comment: "Title for the view in charge of inserting media into an article")
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "Group 5056 (1).svg",
5+
"idiom" : "universal"
6+
}
7+
],
8+
"info" : {
9+
"author" : "xcode",
10+
"version" : 1
11+
},
12+
"properties" : {
13+
"preserves-vector-representation" : true
14+
}
15+
}

WMFComponents/Sources/WMFComponents/Assets.xcassets/empty_activity_tab.imageset/Group 5056 (1).svg

Lines changed: 43 additions & 0 deletions
Loading
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import WMFData
2+
import SwiftUI
3+
import Combine
4+
5+
@MainActor
6+
public final class WMFActivityTabCustomizeViewModel: ObservableObject {
7+
let localizedStrings: LocalizedStrings
8+
9+
@Published var isTimeSpentReadingOn: Bool = true
10+
@Published var isReadingInsightsOn: Bool = true
11+
@Published var isEditingInsightsOn: Bool = true
12+
@Published var isTimelineOfBehaviorOn: Bool = true
13+
var isLoggedIn: Bool {
14+
didSet {
15+
Task {
16+
await updateFromDataController()
17+
}
18+
}
19+
}
20+
var presentLoggedInToastAction: (() -> Void)?
21+
22+
private let dataController = WMFActivityTabDataController.shared
23+
24+
private var cancellables = Set<AnyCancellable>()
25+
26+
public init(localizedStrings: LocalizedStrings, isLoggedIn: Bool) {
27+
self.localizedStrings = localizedStrings
28+
self.isLoggedIn = isLoggedIn
29+
self.presentLoggedInToastAction = nil
30+
31+
Task {
32+
await updateFromDataController()
33+
bindPublishedProperties()
34+
}
35+
}
36+
37+
private func updateFromDataController() async {
38+
let isTimeSpentReadingOn = isLoggedIn ? await dataController.isTimeSpentReadingOn : false
39+
let isReadingInsightsOn = isLoggedIn ? await dataController.isReadingInsightsOn : false
40+
let isEditingInsightsOn = isLoggedIn ? await dataController.isEditingInsightsOn : false
41+
let isTimelineOfBehaviorOn: Bool
42+
43+
if isLoggedIn {
44+
isTimelineOfBehaviorOn = await dataController.isTimelineOfBehaviorOnLoggedIn
45+
} else {
46+
isTimelineOfBehaviorOn = await dataController.isTimelineOfBehaviorOnLoggedOut
47+
}
48+
49+
self.isTimeSpentReadingOn = isTimeSpentReadingOn
50+
self.isReadingInsightsOn = isReadingInsightsOn
51+
self.isEditingInsightsOn = isEditingInsightsOn
52+
self.isTimelineOfBehaviorOn = isTimelineOfBehaviorOn
53+
}
54+
55+
private func bindPublishedProperties() {
56+
$isTimeSpentReadingOn
57+
.sink { [weak self] value in
58+
guard let self, self.isLoggedIn else { return }
59+
60+
Task {
61+
await self.dataController.updateIsTimeSpentReadingOn(value)
62+
}
63+
64+
}
65+
.store(in: &cancellables)
66+
67+
$isReadingInsightsOn
68+
.sink { [weak self] value in
69+
guard let self, self.isLoggedIn else { return }
70+
71+
Task {
72+
await self.dataController.updateIsReadingInsightsOn(value)
73+
}
74+
75+
}
76+
.store(in: &cancellables)
77+
78+
$isEditingInsightsOn
79+
.sink { [weak self] value in
80+
guard let self, self.isLoggedIn else { return }
81+
82+
Task {
83+
await self.dataController.updateIsEditingInsightsOn(value)
84+
}
85+
86+
}
87+
.store(in: &cancellables)
88+
89+
$isTimelineOfBehaviorOn
90+
.sink { [weak self] value in
91+
guard let self else { return }
92+
Task {
93+
if self.isLoggedIn {
94+
await self.dataController.updateIsTimelineOfBehaviorOnLoggedIn(value)
95+
} else {
96+
await self.dataController.updateIsTimelineOfBehaviorOnLoggedOut(value)
97+
}
98+
}
99+
100+
}
101+
.store(in: &cancellables)
102+
}
103+
104+
public struct LocalizedStrings {
105+
let timeSpentReading: String
106+
let readingInsights: String
107+
let editingInsights: String
108+
let allTimeImpact: String
109+
let lastInAppDonation: String
110+
let timeline: String
111+
let footer: String
112+
113+
public init(timeSpentReading: String, readingInsights: String, editingInsights: String, allTimeImpact: String, lastInAppDonation: String, timeline: String, footer: String) {
114+
self.timeSpentReading = timeSpentReading
115+
self.readingInsights = readingInsights
116+
self.editingInsights = editingInsights
117+
self.allTimeImpact = allTimeImpact
118+
self.lastInAppDonation = lastInAppDonation
119+
self.timeline = timeline
120+
self.footer = footer
121+
}
122+
}
123+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import SwiftUI
2+
import Foundation
3+
import WMFData
4+
5+
public struct WMFActivityTabCustomizeView: View {
6+
@ObservedObject var appEnvironment = WMFAppEnvironment.current
7+
@ObservedObject public var viewModel: WMFActivityTabCustomizeViewModel
8+
9+
public init(viewModel: WMFActivityTabCustomizeViewModel) {
10+
self.viewModel = viewModel
11+
}
12+
13+
var theme: WMFTheme {
14+
appEnvironment.theme
15+
}
16+
17+
public var body: some View {
18+
Form {
19+
Section {
20+
Toggle(
21+
viewModel.localizedStrings.timeSpentReading,
22+
isOn: $viewModel.isTimeSpentReadingOn
23+
)
24+
.listRowBackground(Color(theme.paperBackground).edgesIgnoringSafeArea([.all]))
25+
.onChange(of: viewModel.isTimeSpentReadingOn) { newValue in
26+
guard newValue == true else { return }
27+
guard viewModel.isLoggedIn else {
28+
viewModel.isTimeSpentReadingOn = false
29+
viewModel.presentLoggedInToastAction?()
30+
return
31+
}
32+
}
33+
34+
Toggle(
35+
viewModel.localizedStrings.readingInsights,
36+
isOn: $viewModel.isReadingInsightsOn
37+
)
38+
.listRowBackground(Color(theme.paperBackground).edgesIgnoringSafeArea([.all]))
39+
.onChange(of: viewModel.isReadingInsightsOn) { newValue in
40+
guard newValue == true else { return }
41+
guard viewModel.isLoggedIn else {
42+
viewModel.isReadingInsightsOn = false
43+
viewModel.presentLoggedInToastAction?()
44+
return
45+
}
46+
}
47+
48+
Toggle(
49+
viewModel.localizedStrings.editingInsights,
50+
isOn: $viewModel.isEditingInsightsOn
51+
)
52+
.listRowBackground(Color(theme.paperBackground).edgesIgnoringSafeArea([.all]))
53+
.onChange(of: viewModel.isEditingInsightsOn) { newValue in
54+
guard newValue == true else { return }
55+
guard viewModel.isLoggedIn else {
56+
viewModel.isEditingInsightsOn = false
57+
viewModel.presentLoggedInToastAction?()
58+
return
59+
}
60+
}
61+
62+
Toggle(
63+
viewModel.localizedStrings.timeline,
64+
isOn: $viewModel.isTimelineOfBehaviorOn
65+
)
66+
.listRowBackground(Color(theme.paperBackground).edgesIgnoringSafeArea([.all]))
67+
} footer: {
68+
Text(viewModel.localizedStrings.footer)
69+
.font(Font(WMFFont.for(.caption1)))
70+
.foregroundStyle(Color(uiColor: theme.secondaryText))
71+
}
72+
}
73+
.listRowBackground(Color(theme.paperBackground).edgesIgnoringSafeArea([.all]))
74+
.listStyle(GroupedListStyle())
75+
.listBackgroundColor(Color(theme.baseBackground))
76+
.onAppear(perform: {
77+
if #unavailable(iOS 16) {
78+
UITableView.appearance().backgroundColor = UIColor.clear
79+
}
80+
})
81+
.onDisappear(perform: {
82+
if #unavailable(iOS 16) {
83+
UITableView.appearance().backgroundColor = UIColor.systemGroupedBackground
84+
}
85+
})
86+
}
87+
}

WMFComponents/Sources/WMFComponents/Components/Activity Tab/WMFActivityTabView.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,11 @@ public struct WMFActivityTabView: View {
8282
// if let mostViewedArticlesViewModel = viewModel.mostViewedArticlesViewModel {
8383
// MostViewedArticlesView(viewModel: mostViewedArticlesViewModel)
8484
// }
85-
//
85+
//
8686
// if let contributionsViewModel = viewModel.contributionsViewModel {
8787
// ContributionsView(viewModel: contributionsViewModel)
8888
// }
89-
//
89+
//
9090
// if viewModel.allTimeImpactViewModel != nil || viewModel.recentActivityViewModel != nil || viewModel.articleViewsViewModel != nil {
9191
// CombinedImpactView(allTimeImpactViewModel: viewModel.allTimeImpactViewModel, recentActivityViewModel: viewModel.recentActivityViewModel, articleViewsViewModel: viewModel.articleViewsViewModel)
9292
// }

WMFComponents/Sources/WMFComponents/Components/Activity Tab/WMFActivityTabViewModel.swift

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ public final class WMFActivityTabViewModel: ObservableObject {
1313

1414
public var savedArticlesModuleDataDelegate: SavedArticleModuleDataDelegate?
1515
public var didTapPrimaryLoggedOutCTA: (() -> Void)?
16+
public var presentCustomizeLogInToastAction: (() -> Void)? {
17+
didSet {
18+
self.customizeViewModel.presentLoggedInToastAction = self.presentCustomizeLogInToastAction
19+
}
20+
}
1621

1722
// MARK: - Localization
1823

@@ -44,8 +49,16 @@ public final class WMFActivityTabViewModel: ObservableObject {
4449
public let emptyViewSubtitleLoggedIn: String
4550
public let emptyViewTitleLoggedOut: String
4651
public let emptyViewSubtitleLoggedOut: String
47-
48-
public init(userNamesReading: @escaping (String) -> String, noUsernameReading: String, totalHoursMinutesRead: @escaping (Int, Int) -> String, onWikipediaiOS: String, timeSpentReading: String, totalArticlesRead: String, week: String, articlesRead: String, topCategories: String, articlesSavedTitle: String, remaining: @escaping (Int) -> String, loggedOutTitle: String, loggedOutSubtitle: String, loggedOutPrimaryCTA: String, yourImpact: String, todayTitle: String, yesterdayTitle: String, openArticle: String, deleteAccessibilityLabel: String, totalEdits: String, read: String, edited: String, saved: String, emptyViewTitleLoggedIn: String, emptyViewSubtitleLoggedIn: String, emptyViewTitleLoggedOut: String, emptyViewSubtitleLoggedOut: String) {
52+
public let customizeTimeSpentReading: String
53+
public let customizeReadingInsights: String
54+
public let customizeEditingInsights: String
55+
public let customizeAllTimeImpact: String
56+
public let customizeLastInAppDonation: String
57+
public let customizeTimelineOfBehavior: String
58+
public let customizeFooter: String
59+
public let customizeEmptyState: String
60+
61+
public init(userNamesReading: @escaping (String) -> String, noUsernameReading: String, totalHoursMinutesRead: @escaping (Int, Int) -> String, onWikipediaiOS: String, timeSpentReading: String, totalArticlesRead: String, week: String, articlesRead: String, topCategories: String, articlesSavedTitle: String, remaining: @escaping (Int) -> String, loggedOutTitle: String, loggedOutSubtitle: String, loggedOutPrimaryCTA: String, yourImpact: String, todayTitle: String, yesterdayTitle: String, openArticle: String, deleteAccessibilityLabel: String, totalEdits: String, read: String, edited: String, saved: String, emptyViewTitleLoggedIn: String, emptyViewSubtitleLoggedIn: String, emptyViewTitleLoggedOut: String, emptyViewSubtitleLoggedOut: String, customizeTimeSpentReading: String, customizeReadingInsights: String, customizeEditingInsights: String, customizeAllTimeImpact: String, customizeLastInAppDonation: String, customizeTimelineOfBehavior: String, customizeFooter: String, customizeEmptyState: String) {
4962
self.userNamesReading = userNamesReading
5063
self.noUsernameReading = noUsernameReading
5164
self.totalHoursMinutesRead = totalHoursMinutesRead
@@ -73,6 +86,14 @@ public final class WMFActivityTabViewModel: ObservableObject {
7386
self.emptyViewSubtitleLoggedIn = emptyViewSubtitleLoggedIn
7487
self.emptyViewTitleLoggedOut = emptyViewTitleLoggedOut
7588
self.emptyViewSubtitleLoggedOut = emptyViewSubtitleLoggedOut
89+
self.customizeTimeSpentReading = customizeTimeSpentReading
90+
self.customizeReadingInsights = customizeReadingInsights
91+
self.customizeEditingInsights = customizeEditingInsights
92+
self.customizeAllTimeImpact = customizeAllTimeImpact
93+
self.customizeLastInAppDonation = customizeLastInAppDonation
94+
self.customizeTimelineOfBehavior = customizeTimelineOfBehavior
95+
self.customizeFooter = customizeFooter
96+
self.customizeEmptyState = customizeEmptyState
7697
}
7798
}
7899

@@ -91,13 +112,17 @@ public final class WMFActivityTabViewModel: ObservableObject {
91112
@Published var articleViewsViewModel: ArticleViewsViewModel?
92113
@Published public var timelineViewModel: TimelineViewModel
93114
@Published public var emptyViewModel: WMFEmptyViewModel
115+
@Published public var customizeViewModel: WMFActivityTabCustomizeViewModel
94116
@Published public var shouldShowLogInPrompt: Bool = false
95117
@Published var sections: [TimelineViewModel.TimelineSection] = []
96118

97119
@Published var globalEditCount: Int?
98120
public var isEmpty: Bool = false
99121
public var onTapGlobalEdits: (() -> Void)?
100122
public var fetchDataCompleteAction: ((Bool) -> Void)?
123+
public var openCustomize: () -> Void = { }
124+
125+
private var cancellables = Set<AnyCancellable>()
101126

102127
// MARK: - Init
103128

@@ -131,6 +156,16 @@ public final class WMFActivityTabViewModel: ObservableObject {
131156

132157
self.emptyViewModel = Self.generateEmptyViewModel(localizedStrings: localizedStrings, isLoggedIn: authenticationState == .loggedIn)
133158

159+
let customizeViewModel = WMFActivityTabCustomizeViewModel(localizedStrings: WMFActivityTabCustomizeViewModel.LocalizedStrings(timeSpentReading: localizedStrings.customizeTimeSpentReading, readingInsights: localizedStrings.customizeReadingInsights, editingInsights: localizedStrings.customizeEditingInsights, allTimeImpact: localizedStrings.customizeAllTimeImpact, lastInAppDonation: localizedStrings.customizeLastInAppDonation, timeline: localizedStrings.customizeTimelineOfBehavior, footer: localizedStrings.customizeFooter), isLoggedIn: authenticationState == .loggedIn)
160+
self.customizeViewModel = customizeViewModel
161+
162+
// Unfortunately this part is needed for SwiftUI view to see changes in binding. Alternative is to have the toggle booleans live here within WMFActivityTabViewModel
163+
customizeViewModel.objectWillChange
164+
.sink { [weak self] _ in
165+
self?.objectWillChange.send()
166+
}
167+
.store(in: &cancellables)
168+
134169
self.timelineViewModel.activityTabViewModel = self
135170

136171
Task {
@@ -228,6 +263,7 @@ public final class WMFActivityTabViewModel: ObservableObject {
228263
if self.authenticationState != .loggedIn {
229264
globalEditCount = nil
230265
}
266+
self.customizeViewModel.isLoggedIn = authState == .loggedIn
231267
}
232268

233269
public var hoursMinutesRead: String {

0 commit comments

Comments
 (0)