Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notification Filtering (IOS-241) #1319

Merged
merged 48 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
cf39ad6
Add notification-policy-download (IOS-241)
zeitschlag Jun 24, 2024
3118861
Add a filtering-button that is only visible if there's a policy (IOS-…
zeitschlag Jun 25, 2024
f297cba
Fix detens (IOS-241)
zeitschlag Jun 25, 2024
4843348
Add and show filter-banner in "everything"-notifications (if there ar…
zeitschlag Jun 25, 2024
151880f
Add basic UI filter notification-filtering-setting (IOS-241)
zeitschlag Jul 8, 2024
0b74981
Show subtitle (IOS-241)
zeitschlag Jul 8, 2024
94ad86c
Dependencyinject ViewModel based on downloaded policy-data (IOS-241)
zeitschlag Jul 8, 2024
363a521
UI/UX-improvements (IOS-241)
zeitschlag Jul 9, 2024
847cadb
Upload updated policy (IOS-241)
zeitschlag Jul 9, 2024
7cdde77
Download notification-requests (IOS-241)
zeitschlag Jul 9, 2024
5473e7f
Add viewController for notification-requests (IOS-241)
zeitschlag Jul 9, 2024
b119a6d
Use correct properties for account (IOS-241)
zeitschlag Jul 9, 2024
05a08f6
Use better icon after feedback (IOS-241)
zeitschlag Jul 9, 2024
99f2fc9
More feedback from Sam (IOS-241)
zeitschlag Jul 9, 2024
ed4a003
Style cells according to Sams feedback (IOS-241)
zeitschlag Jul 9, 2024
c79e26b
Refactor code to coordinator (IOS-241)
zeitschlag Jul 10, 2024
6702f06
Implement special sheet header (IOS-241)
zeitschlag Jul 10, 2024
65075c5
"Fix" content inset (IOS-241)
zeitschlag Jul 10, 2024
7c6c9b5
Use SymbolConfigurations for buttons (IOS-241)
zeitschlag Jul 10, 2024
4cf75d3
Show account for request (IOS-241)
zeitschlag Jul 10, 2024
0d5dd51
[WIP] Prepare enum-cases to show notifications from one account (IOS-…
zeitschlag Jul 17, 2024
b424dab
Fix build issues that happened after rebase (IOS-241)
zeitschlag Jul 17, 2024
fb3dfb5
Show notifications from one account (IOS-241)
zeitschlag Jul 17, 2024
8115aea
Some more UI-feedback (IOS-241)
zeitschlag Jul 17, 2024
1a123d0
Set title and fix background color (IOS-241)
zeitschlag Jul 18, 2024
e645bc1
Show accept/reject-button (IOS-241)
zeitschlag Jul 18, 2024
94a8791
[WIP]: Add spinner and do something when pressing the button (IOS-241)
zeitschlag Jul 18, 2024
8b65421
[WIP] Add API-calls for accepting/rejecting notification requests (IO…
zeitschlag Jul 18, 2024
55d6b28
Use correct URL :facepalm: (IOS-241)
zeitschlag Jul 18, 2024
5925d84
Accept/reject notification requests (IOS-241)
zeitschlag Jul 18, 2024
67e1e5e
Minor cleanup (IOS-241)
zeitschlag Jul 19, 2024
48f5864
Add first draft for Notification-count-view (IOS-241)
zeitschlag Jul 22, 2024
d87deff
Update notifications after policy/request-change (IOS-241)
zeitschlag Jul 23, 2024
25f15d2
Update policies (IOS-241)
zeitschlag Jul 23, 2024
9e8de73
Localization (IOS-241)
zeitschlag Jul 23, 2024
ffc8026
Localization for Banner (IOS-241)
zeitschlag Jul 23, 2024
287587f
[WIP] Plural Localization for banner-subtitle (IOS-241)
zeitschlag Jul 23, 2024
f5bc847
Reverse buttons (IOS-241)
zeitschlag Jul 23, 2024
ff48cc2
Add swipe-action to dismiss (IOS-241)
zeitschlag Jul 24, 2024
f3c035c
Add accept/dismiss-menu (IOS-241)
zeitschlag Jul 24, 2024
b3bfa51
Sprinkle in some, you guessed it: Localization (IOS-241)
zeitschlag Jul 24, 2024
86aa92d
Cleanup (IOS-241)
zeitschlag Jul 24, 2024
2d05945
Add chevron (IOS-241)
zeitschlag Jul 24, 2024
1bf763a
Add an english translation (IOS-241)
zeitschlag Jul 24, 2024
9da5b99
Show modal on ipad (IOS-241)
zeitschlag Jul 24, 2024
8d535be
Fix bottom margins as requested (IOS-241)
zeitschlag Jul 24, 2024
766e631
Update policy after changes
zeitschlag Jul 24, 2024
bac8c23
Adress feedback (IOS-241)
zeitschlag Jul 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,178 changes: 597 additions & 581 deletions Localization/Localizable.stringsdict

Large diffs are not rendered by default.

1,178 changes: 597 additions & 581 deletions Localization/StringsConvertor/input/Base.lproj/Localizable.stringsdict

Large diffs are not rendered by default.

34 changes: 33 additions & 1 deletion Localization/StringsConvertor/input/Base.lproj/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,15 @@
"title": "Home",
"timeline_menu": {
"following": "Following",
"local_community": "Local"
"local_community": "Local",
"lists": {
"title": "Lists",
"empty_message": "You don't have any Lists"
},
"hashtags": {
"title": "Followed Hashtags",
"empty_message": "You don't follow any Hashtags"
}
},
"timeline_pill": {
"offline": "Offline",
Expand Down Expand Up @@ -744,6 +752,30 @@
"silence": "Your account has been limited.",
"suspend": "Your account has been suspended.",
"learn_more": "Learn More"
},
"filtered_notification": {
"title": "Filtered Notifications",
"accept": "Accept",
"dismiss": "Dismiss",
},
"policy": {
"title": "Filter Notifications from…",
"not_following": {
"title": "People you don't follow",
"subtitle": "Until you manually approve them"
},
"no_follower": {
"title": "People not following you",
"subtitle": "Including people who have been following you fewer than 3 days"
},
"new_account": {
"title": "New accounts",
"subtitle": "Created within the past 30 days"
},
"private_mentions": {
"title": "Unsolicited private mentions",
"subtitle": "Filtered unless it’s in reply to your own mention or if you follow the sender"
}
}
},
"thread": {
Expand Down
24 changes: 24 additions & 0 deletions Localization/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,30 @@
"silence": "Your account has been limited.",
"suspend": "Your account has been suspended.",
"learn_more": "Learn More"
},
"filtered_notification": {
"title": "Filtered Notifications",
"accept": "Accept",
"dismiss": "Dismiss",
},
"policy": {
"title": "Filter Notifications from…",
"not_following": {
"title": "People you don't follow",
"subtitle": "Until you manually approve them"
},
"no_follower": {
"title": "People not following you",
"subtitle": "Including people who have been following you fewer than 3 days"
},
"new_account": {
"title": "New accounts",
"subtitle": "Created within the past 30 days"
},
"private_mentions": {
"title": "Unsolicited private mentions",
"subtitle": "Filtered unless it’s in reply to your own mention or if you follow the sender"
}
}
},
"thread": {
Expand Down
65 changes: 65 additions & 0 deletions Mastodon.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

13 changes: 12 additions & 1 deletion Mastodon/Coordinator/SceneCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,12 @@ extension SceneCoordinator {

// setting
case settings(setting: Setting)


// Notifications
case notificationPolicy(viewModel: NotificationFilterViewModel)
case notificationRequests(viewModel: NotificationRequestsViewModel)
case accountNotificationTimeline(viewModel: NotificationTimelineViewModel, request: Mastodon.Entity.NotificationRequest)

// report
case report(viewModel: ReportViewModel)
case reportServerRules(viewModel: ReportServerRulesViewModel)
Expand Down Expand Up @@ -558,6 +563,12 @@ private extension SceneCoordinator {
case .editStatus(let viewModel):
let composeViewController = ComposeViewController(viewModel: viewModel)
viewController = composeViewController
case .notificationRequests(let viewModel):
viewController = NotificationRequestsTableViewController(viewModel: viewModel)
case .notificationPolicy(let viewModel):
viewController = NotificationPolicyViewController(viewModel: viewModel)
case .accountNotificationTimeline(let viewModel, let request):
viewController = AccountNotificationTimelineViewController(viewModel: viewModel, context: appContext, coordinator: self, notificationRequest: request)
}

setupDependency(for: viewController as? NeedsDependency)
Expand Down
52 changes: 52 additions & 0 deletions Mastodon/Protocol/Provider/DataSourceFacade+Notifications.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright © 2024 Mastodon gGmbH. All rights reserved.

import Foundation
import MastodonCore
import MastodonSDK

extension DataSourceFacade {
@MainActor
static func coordinateToNotificationRequests(
provider: DataSourceProvider & AuthContextProvider
) async {
provider.coordinator.showLoading()

do {
let notificationRequests = try await provider.context.apiService.notificationRequests(authenticationBox: provider.authContext.mastodonAuthenticationBox).value
let viewModel = NotificationRequestsViewModel(appContext: provider.context, authContext: provider.authContext, coordinator: provider.coordinator, requests: notificationRequests)

provider.coordinator.hideLoading()

let transition: SceneCoordinator.Transition

if provider.traitCollection.userInterfaceIdiom == .phone {
transition = .show
} else {
transition = .modal(animated: true)
}

provider.coordinator.present(scene: .notificationRequests(viewModel: viewModel), transition: transition)
} catch {
//TODO: Error Handling
zeitschlag marked this conversation as resolved.
Show resolved Hide resolved
provider.coordinator.hideLoading()
}
}

@MainActor
static func coordinateToNotificationRequest(
request: Mastodon.Entity.NotificationRequest,
provider: ViewControllerWithDependencies & AuthContextProvider
) async -> AccountNotificationTimelineViewController? {
provider.coordinator.showLoading()

let notificationTimelineViewModel = NotificationTimelineViewModel(context: provider.context, authContext: provider.authContext, scope: .fromAccount(request.account))

provider.coordinator.hideLoading()

guard let viewController = provider.coordinator.present(scene: .accountNotificationTimeline(viewModel: notificationTimelineViewModel, request: request), transition: .show) as? AccountNotificationTimelineViewController else { return nil }

return viewController

}

}
44 changes: 21 additions & 23 deletions Mastodon/Protocol/Provider/DataSourceFacade+SearchHistory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,30 @@ extension DataSourceFacade {
item: DataSourceItem
) async {
switch item {
case .account(account: let account, relationship: _):
let now = Date()
let userID = provider.authContext.mastodonAuthenticationBox.userID
let searchEntry = Persistence.SearchHistory.Item(
updatedAt: now,
userID: userID,
account: account,
hashtag: nil
)
case .account(account: let account, relationship: _):
let now = Date()
let userID = provider.authContext.mastodonAuthenticationBox.userID
let searchEntry = Persistence.SearchHistory.Item(
updatedAt: now,
userID: userID,
account: account,
hashtag: nil
)

try? FileManager.default.addSearchItem(searchEntry, for: provider.authContext.mastodonAuthenticationBox)
case .hashtag(let tag):
try? FileManager.default.addSearchItem(searchEntry, for: provider.authContext.mastodonAuthenticationBox)
case .hashtag(let tag):

let now = Date()
let userID = provider.authContext.mastodonAuthenticationBox.userID
let searchEntry = Persistence.SearchHistory.Item(
updatedAt: now,
userID: userID,
account: nil,
hashtag: tag
)
let now = Date()
let userID = provider.authContext.mastodonAuthenticationBox.userID
let searchEntry = Persistence.SearchHistory.Item(
updatedAt: now,
userID: userID,
account: nil,
hashtag: tag
)

try? FileManager.default.addSearchItem(searchEntry, for: provider.authContext.mastodonAuthenticationBox)
case .status:
break
case .notification:
try? FileManager.default.addSearchItem(searchEntry, for: provider.authContext.mastodonAuthenticationBox)
case .status, .notification, .notificationBanner(_):
break

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,10 +514,9 @@ extension NotificationTableViewCellDelegate where Self: DataSourceProvider & Aut
)
case .account(let account, _):
await DataSourceFacade.coordinateToProfileScene(provider: self, account: account)
case .notification:
assertionFailure("TODO")
case .hashtag(_):
assertionFailure("TODO")
case .notification, .hashtag(_), .notificationBanner(_):
// not supposed to happen
break
}
} // end Task
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -618,10 +618,9 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte
provider: self,
account: account
)
case .notification:
assertionFailure("TODO")
case .hashtag(_):
assertionFailure("TODO")
case .notification, .hashtag(_), .notificationBanner(_):
// not supposed to happen
break
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,42 +22,44 @@ extension UITableViewDelegate where Self: DataSourceProvider & AuthContextProvid
return
}
switch item {
case .account(let account, relationship: _):
await DataSourceFacade.coordinateToProfileScene(provider: self, account: account)
case .status(let status):
case .account(let account, relationship: _):
await DataSourceFacade.coordinateToProfileScene(provider: self, account: account)
case .status(let status):
await DataSourceFacade.coordinateToStatusThreadScene(
provider: self,
target: .status, // remove reblog wrapper
status: status
)
case .hashtag(let tag):
await DataSourceFacade.coordinateToHashtagScene(
provider: self,
tag: tag
)
case .notification(let notification):
let _status: MastodonStatus? = notification.status
if let status = _status {
await DataSourceFacade.coordinateToStatusThreadScene(
provider: self,
target: .status, // remove reblog wrapper
status: status
)
case .hashtag(let tag):
await DataSourceFacade.coordinateToHashtagScene(
provider: self,
tag: tag
} else if let accountWarning = notification.entity.accountWarning {
let url = Mastodon.API.disputesEndpoint(domain: authContext.mastodonAuthenticationBox.domain, strikeId: accountWarning.id)
_ = coordinator.present(
scene: .safari(url: url),
from: self,
transition: .safariPresent(animated: true, completion: nil)
)
case .notification(let notification):
let _status: MastodonStatus? = notification.status
if let status = _status {
await DataSourceFacade.coordinateToStatusThreadScene(
provider: self,
target: .status, // remove reblog wrapper
status: status
)
} else if let accountWarning = notification.entity.accountWarning {
let url = Mastodon.API.disputesEndpoint(domain: authContext.mastodonAuthenticationBox.domain, strikeId: accountWarning.id)
_ = coordinator.present(
scene: .safari(url: url),
from: self,
transition: .safariPresent(animated: true, completion: nil)
)

} else {
await DataSourceFacade.coordinateToProfileScene(
provider: self,
account: notification.entity.account
)
} // end Task
} // end func
} else {
await DataSourceFacade.coordinateToProfileScene(
provider: self,
account: notification.entity.account
)
}
case .notificationBanner(let policy):
await DataSourceFacade.coordinateToNotificationRequests(provider: self)
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions Mastodon/Protocol/Provider/DataSourceProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ enum DataSourceItem: Hashable {
case status(record: MastodonStatus)
case hashtag(tag: Mastodon.Entity.Tag)
case notification(record: MastodonNotification)
case notificationBanner(policy: Mastodon.Entity.NotificationPolicy)
case account(account: Mastodon.Entity.Account, relationship: Mastodon.Entity.Relationship?)
}

Expand Down
Loading
Loading