mirror of
https://github.com/mastodon/mastodon-ios
synced 2025-04-11 22:58:02 +02:00
Implement loading and saving of privacy & safety preferences (IOS-168)
This commit is contained in:
parent
f31fd74abe
commit
d77e8d1eac
@ -1,5 +1,6 @@
|
|||||||
// Copyright © 2024 Mastodon gGmbH. All rights reserved.
|
// Copyright © 2024 Mastodon gGmbH. All rights reserved.
|
||||||
|
|
||||||
|
import Combine
|
||||||
import UIKit
|
import UIKit
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
@ -7,14 +8,24 @@ import MastodonCore
|
|||||||
import MastodonLocalization
|
import MastodonLocalization
|
||||||
import MastodonAsset
|
import MastodonAsset
|
||||||
|
|
||||||
final class PrivacySafetyViewController: UIHostingController<PrivacySafetyView>, NeedsDependency {
|
final class PrivacySafetyViewController: UIHostingController<PrivacySafetyView> {
|
||||||
weak var context: AppContext!
|
private let viewModel: PrivacySafetyViewModel
|
||||||
weak var coordinator: SceneCoordinator!
|
private var disposeBag = [AnyCancellable]()
|
||||||
|
|
||||||
init(context: AppContext, coordinator: SceneCoordinator) {
|
init(appContext: AppContext, authContext: AuthContext, coordinator: SceneCoordinator) {
|
||||||
self.context = context
|
self.viewModel = PrivacySafetyViewModel(
|
||||||
self.coordinator = coordinator
|
appContext: appContext, authContext: authContext, coordinator: coordinator
|
||||||
super.init(rootView: PrivacySafetyView(viewModel: PrivacySafetyViewModel()))
|
)
|
||||||
|
super.init(
|
||||||
|
rootView: PrivacySafetyView(
|
||||||
|
viewModel: self.viewModel
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.viewModel.onDismiss.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] _ in
|
||||||
|
self?.dismiss(animated: true)
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor required dynamic init?(coder aDecoder: NSCoder) {
|
@MainActor required dynamic init?(coder aDecoder: NSCoder) {
|
||||||
@ -72,6 +83,7 @@ struct PrivacySafetyView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onAppear(perform: viewModel.viewDidAppear)
|
.onAppear(perform: viewModel.viewDidAppear)
|
||||||
|
.onDisappear(perform: viewModel.saveSettings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// Copyright © 2024 Mastodon gGmbH. All rights reserved.
|
// Copyright © 2024 Mastodon gGmbH. All rights reserved.
|
||||||
|
|
||||||
|
import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
import MastodonLocalization
|
import MastodonLocalization
|
||||||
import MastodonCore
|
import MastodonCore
|
||||||
@ -23,10 +24,42 @@ class PrivacySafetyViewModel: ObservableObject {
|
|||||||
return L10n.Scene.Settings.PrivacySafety.DefaultPostVisibility.onlyPeopleMentioned
|
return L10n.Scene.Settings.PrivacySafety.DefaultPostVisibility.onlyPeopleMentioned
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func from(_ privacy: Mastodon.Entity.Source.Privacy) -> Self {
|
||||||
|
switch privacy {
|
||||||
|
case .public:
|
||||||
|
return .public
|
||||||
|
case .unlisted:
|
||||||
|
return .followersOnly
|
||||||
|
case .private, .direct:
|
||||||
|
return .onlyPeopleMentioned
|
||||||
|
case ._other(_):
|
||||||
|
return .public
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toPrivacy() -> Mastodon.Entity.Source.Privacy {
|
||||||
|
switch self {
|
||||||
|
case .public:
|
||||||
|
return .public
|
||||||
|
case .followersOnly:
|
||||||
|
return .unlisted
|
||||||
|
case .onlyPeopleMentioned:
|
||||||
|
return .private
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
weak var appContext: AppContext?
|
private var appContext: AppContext?
|
||||||
|
private var authContext: AuthContext?
|
||||||
|
private var coordinator: SceneCoordinator?
|
||||||
|
|
||||||
|
init(appContext: AppContext?, authContext: AuthContext?, coordinator: SceneCoordinator?) {
|
||||||
|
self.appContext = appContext
|
||||||
|
self.authContext = authContext
|
||||||
|
self.coordinator = coordinator
|
||||||
|
}
|
||||||
|
|
||||||
@Published var preset: Preset = .openPublic {
|
@Published var preset: Preset = .openPublic {
|
||||||
didSet { applyPreset(preset) }
|
didSet { applyPreset(preset) }
|
||||||
}
|
}
|
||||||
@ -52,6 +85,7 @@ class PrivacySafetyViewModel: ObservableObject {
|
|||||||
|
|
||||||
private var doNotEvaluate = true
|
private var doNotEvaluate = true
|
||||||
@Published var isInitialized = false
|
@Published var isInitialized = false
|
||||||
|
let onDismiss = PassthroughSubject<Void, Never>()
|
||||||
|
|
||||||
func viewDidAppear() {
|
func viewDidAppear() {
|
||||||
doNotEvaluate = false
|
doNotEvaluate = false
|
||||||
@ -96,23 +130,63 @@ extension PrivacySafetyViewModel {
|
|||||||
|
|
||||||
func loadSettings() {
|
func loadSettings() {
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
|
guard let appContext, let authContext else {
|
||||||
|
return dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
let domain = authContext.mastodonAuthenticationBox.domain
|
||||||
|
let userAuthorization = authContext.mastodonAuthenticationBox.userAuthorization
|
||||||
|
|
||||||
|
let account = try await appContext.apiService.accountVerifyCredentials(
|
||||||
|
domain: domain,
|
||||||
|
authorization: userAuthorization
|
||||||
|
).singleOutput().value
|
||||||
|
|
||||||
|
if let privacy = account.source?.privacy {
|
||||||
|
visibility = .from(privacy)
|
||||||
|
}
|
||||||
|
|
||||||
|
manuallyApproveFollowRequests = account.locked == true
|
||||||
|
showFollowersAndFollowing = account.hideCollections == false
|
||||||
|
suggestMyAccountToOthers = account.discoverable == true
|
||||||
|
appearInSearches = account.indexable == true
|
||||||
|
|
||||||
isInitialized = true
|
isInitialized = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveSettings() {
|
func saveSettings() {
|
||||||
Task {
|
Task {
|
||||||
|
guard let appContext, let authContext else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let domain = authContext.mastodonAuthenticationBox.domain
|
||||||
|
let userAuthorization = authContext.mastodonAuthenticationBox.userAuthorization
|
||||||
|
|
||||||
|
let _ = try await appContext.apiService.accountUpdateCredentials(
|
||||||
|
domain: domain,
|
||||||
|
query: .init(
|
||||||
|
discoverable: suggestMyAccountToOthers,
|
||||||
|
locked: manuallyApproveFollowRequests,
|
||||||
|
source: .withPrivacy(visibility.toPrivacy()),
|
||||||
|
indexable: appearInSearches,
|
||||||
|
hideCollections: !showFollowersAndFollowing
|
||||||
|
),
|
||||||
|
authorization: userAuthorization
|
||||||
|
).value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dismiss() {
|
||||||
|
onDismiss.send(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preset Rules Definition
|
// Preset Rules Definition
|
||||||
extension PrivacySafetyViewModel {
|
extension PrivacySafetyViewModel {
|
||||||
static let openPublic: PrivacySafetyViewModel = {
|
static let openPublic: PrivacySafetyViewModel = {
|
||||||
let vm = PrivacySafetyViewModel()
|
let vm = PrivacySafetyViewModel(appContext: nil, authContext: nil, coordinator: nil)
|
||||||
vm.visibility = .public
|
vm.visibility = .public
|
||||||
vm.manuallyApproveFollowRequests = false
|
vm.manuallyApproveFollowRequests = false
|
||||||
vm.showFollowersAndFollowing = true
|
vm.showFollowersAndFollowing = true
|
||||||
@ -122,7 +196,7 @@ extension PrivacySafetyViewModel {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
static let privateRestricted: PrivacySafetyViewModel = {
|
static let privateRestricted: PrivacySafetyViewModel = {
|
||||||
let vm = PrivacySafetyViewModel()
|
let vm = PrivacySafetyViewModel(appContext: nil, authContext: nil, coordinator: nil)
|
||||||
vm.visibility = .followersOnly
|
vm.visibility = .followersOnly
|
||||||
vm.manuallyApproveFollowRequests = true
|
vm.manuallyApproveFollowRequests = true
|
||||||
vm.showFollowersAndFollowing = false
|
vm.showFollowersAndFollowing = false
|
||||||
|
@ -72,7 +72,11 @@ extension SettingsCoordinator: SettingsViewControllerDelegate {
|
|||||||
|
|
||||||
navigationController.pushViewController(notificationViewController, animated: true)
|
navigationController.pushViewController(notificationViewController, animated: true)
|
||||||
case .privacySafety:
|
case .privacySafety:
|
||||||
let privacySafetyViewController = PrivacySafetyViewController(context: appContext, coordinator: sceneCoordinator)
|
let privacySafetyViewController = PrivacySafetyViewController(
|
||||||
|
appContext: appContext,
|
||||||
|
authContext: authContext,
|
||||||
|
coordinator: sceneCoordinator
|
||||||
|
)
|
||||||
navigationController.pushViewController(privacySafetyViewController, animated: true)
|
navigationController.pushViewController(privacySafetyViewController, animated: true)
|
||||||
case .serverDetails(let domain):
|
case .serverDetails(let domain):
|
||||||
let serverDetailsViewController = ServerDetailsViewController(domain: domain, appContext: appContext, authContext: authContext, sceneCoordinator: sceneCoordinator)
|
let serverDetailsViewController = ServerDetailsViewController(domain: domain, appContext: appContext, authContext: authContext, sceneCoordinator: sceneCoordinator)
|
||||||
|
@ -159,7 +159,8 @@ extension Mastodon.API.Account {
|
|||||||
public let source: Mastodon.Entity.Source?
|
public let source: Mastodon.Entity.Source?
|
||||||
public let fieldsAttributes: [Mastodon.Entity.Field]?
|
public let fieldsAttributes: [Mastodon.Entity.Field]?
|
||||||
public let indexable: Bool?
|
public let indexable: Bool?
|
||||||
|
public let hideCollections: Bool?
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case discoverable
|
case discoverable
|
||||||
case bot
|
case bot
|
||||||
@ -172,6 +173,7 @@ extension Mastodon.API.Account {
|
|||||||
case source
|
case source
|
||||||
case fieldsAttributes = "fields_attributes"
|
case fieldsAttributes = "fields_attributes"
|
||||||
case indexable
|
case indexable
|
||||||
|
case hideCollections = "hide_collections"
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
@ -184,7 +186,8 @@ extension Mastodon.API.Account {
|
|||||||
locked: Bool? = nil,
|
locked: Bool? = nil,
|
||||||
source: Mastodon.Entity.Source? = nil,
|
source: Mastodon.Entity.Source? = nil,
|
||||||
fieldsAttributes: [Mastodon.Entity.Field]? = nil,
|
fieldsAttributes: [Mastodon.Entity.Field]? = nil,
|
||||||
indexable: Bool? = nil
|
indexable: Bool? = nil,
|
||||||
|
hideCollections: Bool? = nil
|
||||||
) {
|
) {
|
||||||
self.discoverable = discoverable
|
self.discoverable = discoverable
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
@ -196,6 +199,7 @@ extension Mastodon.API.Account {
|
|||||||
self.source = source
|
self.source = source
|
||||||
self.fieldsAttributes = fieldsAttributes
|
self.fieldsAttributes = fieldsAttributes
|
||||||
self.indexable = indexable
|
self.indexable = indexable
|
||||||
|
self.hideCollections = hideCollections
|
||||||
}
|
}
|
||||||
|
|
||||||
var contentType: String? {
|
var contentType: String? {
|
||||||
@ -209,6 +213,7 @@ extension Mastodon.API.Account {
|
|||||||
var body: Data? {
|
var body: Data? {
|
||||||
var data = Data()
|
var data = Data()
|
||||||
|
|
||||||
|
hideCollections.flatMap { data.append(Data.multipart(key: "hide_collections", value: $0)) }
|
||||||
discoverable.flatMap { data.append(Data.multipart(key: "discoverable", value: $0)) }
|
discoverable.flatMap { data.append(Data.multipart(key: "discoverable", value: $0)) }
|
||||||
bot.flatMap { data.append(Data.multipart(key: "bot", value: $0)) }
|
bot.flatMap { data.append(Data.multipart(key: "bot", value: $0)) }
|
||||||
displayName.flatMap { data.append(Data.multipart(key: "display_name", value: $0)) }
|
displayName.flatMap { data.append(Data.multipart(key: "display_name", value: $0)) }
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
//
|
|
||||||
// File.swift
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Created by Marcus Kida on 29.05.24.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
@ -37,6 +37,8 @@ extension Mastodon.Entity {
|
|||||||
public let locked: Bool
|
public let locked: Bool
|
||||||
public let emojis: [Emoji]
|
public let emojis: [Emoji]
|
||||||
public let discoverable: Bool?
|
public let discoverable: Bool?
|
||||||
|
public let hideCollections: Bool?
|
||||||
|
public let indexable: Bool?
|
||||||
|
|
||||||
// Statistical
|
// Statistical
|
||||||
public let createdAt: Date
|
public let createdAt: Date
|
||||||
@ -71,6 +73,8 @@ extension Mastodon.Entity.Account: Codable {
|
|||||||
case locked
|
case locked
|
||||||
case emojis
|
case emojis
|
||||||
case discoverable
|
case discoverable
|
||||||
|
case hideCollections = "hide_collections"
|
||||||
|
case indexable
|
||||||
|
|
||||||
case createdAt = "created_at"
|
case createdAt = "created_at"
|
||||||
case lastStatusAt = "last_status_at"
|
case lastStatusAt = "last_status_at"
|
||||||
|
@ -36,6 +36,10 @@ extension Mastodon.Entity {
|
|||||||
case language
|
case language
|
||||||
case followRequestsCount = "follow_requests_count"
|
case followRequestsCount = "follow_requests_count"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func withPrivacy(_ privacy: Privacy) -> Self {
|
||||||
|
Source(note: "", fields: nil, privacy: privacy, sensitive: nil, language: nil, followRequestsCount: nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user