forked from shadowfacts/Tusker
Move local user accounts to separate package
This commit is contained in:
parent
5471d810c8
commit
247bb31c56
7
Packages/ComposeUI/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
Packages/ComposeUI/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
|
@ -0,0 +1,9 @@
|
||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
/*.xcodeproj
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/config/registries.json
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||||
|
.netrc
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"pins" : [
|
||||||
|
{
|
||||||
|
"identity" : "swift-system",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/apple/swift-system.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "025bcb1165deab2e20d4eaba79967ce73013f496",
|
||||||
|
"version" : "1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "swift-url",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/karwa/swift-url.git",
|
||||||
|
"state" : {
|
||||||
|
"branch" : "main",
|
||||||
|
"revision" : "220f6ab9d8a7e0742f85eb9f21b745942e001ae6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version" : 2
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
// swift-tools-version: 5.7
|
||||||
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "UserAccounts",
|
||||||
|
platforms: [
|
||||||
|
.iOS(.v15),
|
||||||
|
],
|
||||||
|
products: [
|
||||||
|
// Products define the executables and libraries a package produces, and make them visible to other packages.
|
||||||
|
.library(
|
||||||
|
name: "UserAccounts",
|
||||||
|
targets: ["UserAccounts"]),
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
// Dependencies declare other packages that this package depends on.
|
||||||
|
.package(path: "../Pachyderm"),
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||||
|
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
||||||
|
.target(
|
||||||
|
name: "UserAccounts",
|
||||||
|
dependencies: ["Pachyderm"]),
|
||||||
|
.testTarget(
|
||||||
|
name: "UserAccountsTests",
|
||||||
|
dependencies: ["UserAccounts"]),
|
||||||
|
]
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
# UserAccounts
|
||||||
|
|
||||||
|
A description of this package.
|
|
@ -0,0 +1,80 @@
|
||||||
|
//
|
||||||
|
// UserAccountInfo.swift
|
||||||
|
// UserAccounts
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 3/5/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import CryptoKit
|
||||||
|
|
||||||
|
public struct UserAccountInfo: Equatable, Hashable {
|
||||||
|
public let id: String
|
||||||
|
public let instanceURL: URL
|
||||||
|
public let clientID: String
|
||||||
|
public let clientSecret: String
|
||||||
|
public private(set) var username: String!
|
||||||
|
public let accessToken: String
|
||||||
|
|
||||||
|
fileprivate static let tempAccountID = "temp"
|
||||||
|
|
||||||
|
static func id(instanceURL: URL, username: String?) -> String {
|
||||||
|
// We hash the instance host and username to form the account ID
|
||||||
|
// so that account IDs will match across devices, allowing for data syncing and handoff.
|
||||||
|
var hasher = SHA256()
|
||||||
|
hasher.update(data: instanceURL.host!.data(using: .utf8)!)
|
||||||
|
if let username {
|
||||||
|
hasher.update(data: username.data(using: .utf8)!)
|
||||||
|
}
|
||||||
|
return Data(hasher.finalize()).base64EncodedString()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only to be used for temporary MastodonController needed to fetch own account info and create final UserAccountInfo with real username
|
||||||
|
public init(tempInstanceURL instanceURL: URL, clientID: String, clientSecret: String, accessToken: String) {
|
||||||
|
self.id = UserAccountInfo.tempAccountID
|
||||||
|
self.instanceURL = instanceURL
|
||||||
|
self.clientID = clientID
|
||||||
|
self.clientSecret = clientSecret
|
||||||
|
self.accessToken = accessToken
|
||||||
|
}
|
||||||
|
|
||||||
|
init(instanceURL: URL, clientID: String, clientSecret: String, username: String? = nil, accessToken: String) {
|
||||||
|
self.id = UserAccountInfo.id(instanceURL: instanceURL, username: username)
|
||||||
|
self.instanceURL = instanceURL
|
||||||
|
self.clientID = clientID
|
||||||
|
self.clientSecret = clientSecret
|
||||||
|
self.username = username
|
||||||
|
self.accessToken = accessToken
|
||||||
|
}
|
||||||
|
|
||||||
|
init?(userDefaultsDict dict: [String: String]) {
|
||||||
|
guard let id = dict["id"],
|
||||||
|
let instanceURL = dict["instanceURL"],
|
||||||
|
let url = URL(string: instanceURL),
|
||||||
|
let clientID = dict["clientID"],
|
||||||
|
let secret = dict["clientSecret"],
|
||||||
|
let accessToken = dict["accessToken"] else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
self.id = id
|
||||||
|
self.instanceURL = url
|
||||||
|
self.clientID = clientID
|
||||||
|
self.clientSecret = secret
|
||||||
|
self.username = dict["username"]
|
||||||
|
self.accessToken = accessToken
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A filename-safe string for this account
|
||||||
|
public var persistenceKey: String {
|
||||||
|
// slashes are not allowed in the persistent store coordinator name
|
||||||
|
id.replacingOccurrences(of: "/", with: "_")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func hash(into hasher: inout Hasher) {
|
||||||
|
hasher.combine(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: UserAccountInfo, rhs: UserAccountInfo) -> Bool {
|
||||||
|
return lhs.id == rhs.id
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,16 @@
|
||||||
//
|
//
|
||||||
// LocalData.swift
|
// UserAccountsManager.swift
|
||||||
// Tusker
|
// UserAccounts
|
||||||
//
|
//
|
||||||
// Created by Shadowfacts on 8/18/18.
|
// Created by Shadowfacts on 3/5/23.
|
||||||
// Copyright © 2018 Shadowfacts. All rights reserved.
|
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
import CryptoKit
|
|
||||||
|
|
||||||
class LocalData: ObservableObject {
|
public class UserAccountsManager: ObservableObject {
|
||||||
|
|
||||||
static let shared = LocalData()
|
public static let shared = UserAccountsManager()
|
||||||
|
|
||||||
let defaults: UserDefaults
|
let defaults: UserDefaults
|
||||||
|
|
||||||
|
@ -38,7 +36,7 @@ class LocalData: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
private let accountsKey = "accounts"
|
private let accountsKey = "accounts"
|
||||||
private(set) var accounts: [UserAccountInfo] {
|
public private(set) var accounts: [UserAccountInfo] {
|
||||||
get {
|
get {
|
||||||
if let array = defaults.array(forKey: accountsKey) as? [[String: String]] {
|
if let array = defaults.array(forKey: accountsKey) as? [[String: String]] {
|
||||||
return array.compactMap(UserAccountInfo.init(userDefaultsDict:))
|
return array.compactMap(UserAccountInfo.init(userDefaultsDict:))
|
||||||
|
@ -66,7 +64,7 @@ class LocalData: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
private let mostRecentAccountKey = "mostRecentAccount"
|
private let mostRecentAccountKey = "mostRecentAccount"
|
||||||
private(set) var mostRecentAccountID: String? {
|
public private(set) var mostRecentAccountID: String? {
|
||||||
get {
|
get {
|
||||||
return defaults.string(forKey: mostRecentAccountKey)
|
return defaults.string(forKey: mostRecentAccountKey)
|
||||||
}
|
}
|
||||||
|
@ -109,13 +107,13 @@ class LocalData: ObservableObject {
|
||||||
usesAccountIDHashes = true
|
usesAccountIDHashes = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Account Management
|
// MARK: Account Management
|
||||||
|
|
||||||
var onboardingComplete: Bool {
|
public var onboardingComplete: Bool {
|
||||||
return !accounts.isEmpty
|
return !accounts.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
func addAccount(instanceURL url: URL, clientID: String, clientSecret secret: String, username: String?, accessToken: String) -> UserAccountInfo {
|
public func addAccount(instanceURL url: URL, clientID: String, clientSecret secret: String, username: String?, accessToken: String) -> UserAccountInfo {
|
||||||
var accounts = self.accounts
|
var accounts = self.accounts
|
||||||
if let index = accounts.firstIndex(where: { $0.instanceURL == url && $0.username == username }) {
|
if let index = accounts.firstIndex(where: { $0.instanceURL == url && $0.username == username }) {
|
||||||
accounts.remove(at: index)
|
accounts.remove(at: index)
|
||||||
|
@ -126,15 +124,15 @@ class LocalData: ObservableObject {
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeAccount(_ info: UserAccountInfo) {
|
public func removeAccount(_ info: UserAccountInfo) {
|
||||||
accounts.removeAll(where: { $0.id == info.id })
|
accounts.removeAll(where: { $0.id == info.id })
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAccount(id: String) -> UserAccountInfo? {
|
public func getAccount(id: String) -> UserAccountInfo? {
|
||||||
return accounts.first(where: { $0.id == id })
|
return accounts.first(where: { $0.id == id })
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMostRecentAccount() -> UserAccountInfo? {
|
public func getMostRecentAccount() -> UserAccountInfo? {
|
||||||
guard onboardingComplete else { return nil }
|
guard onboardingComplete else { return nil }
|
||||||
let mostRecent: UserAccountInfo?
|
let mostRecent: UserAccountInfo?
|
||||||
if let id = mostRecentAccountID {
|
if let id = mostRecentAccountID {
|
||||||
|
@ -145,86 +143,13 @@ class LocalData: ObservableObject {
|
||||||
return mostRecent ?? accounts.first!
|
return mostRecent ?? accounts.first!
|
||||||
}
|
}
|
||||||
|
|
||||||
func setMostRecentAccount(_ account: UserAccountInfo?) {
|
public func setMostRecentAccount(_ account: UserAccountInfo?) {
|
||||||
mostRecentAccountID = account?.id
|
mostRecentAccountID = account?.id
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LocalData {
|
public extension Notification.Name {
|
||||||
struct UserAccountInfo: Equatable, Hashable {
|
|
||||||
let id: String
|
|
||||||
let instanceURL: URL
|
|
||||||
let clientID: String
|
|
||||||
let clientSecret: String
|
|
||||||
private(set) var username: String!
|
|
||||||
let accessToken: String
|
|
||||||
|
|
||||||
fileprivate static let tempAccountID = "temp"
|
|
||||||
|
|
||||||
fileprivate static func id(instanceURL: URL, username: String?) -> String {
|
|
||||||
// We hash the instance host and username to form the account ID
|
|
||||||
// so that account IDs will match across devices, allowing for data syncing and handoff.
|
|
||||||
var hasher = SHA256()
|
|
||||||
hasher.update(data: instanceURL.host!.data(using: .utf8)!)
|
|
||||||
if let username {
|
|
||||||
hasher.update(data: username.data(using: .utf8)!)
|
|
||||||
}
|
|
||||||
return Data(hasher.finalize()).base64EncodedString()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Only to be used for temporary MastodonController needed to fetch own account info and create final UserAccountInfo with real username
|
|
||||||
init(tempInstanceURL instanceURL: URL, clientID: String, clientSecret: String, accessToken: String) {
|
|
||||||
self.id = UserAccountInfo.tempAccountID
|
|
||||||
self.instanceURL = instanceURL
|
|
||||||
self.clientID = clientID
|
|
||||||
self.clientSecret = clientSecret
|
|
||||||
self.accessToken = accessToken
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate init(instanceURL: URL, clientID: String, clientSecret: String, username: String? = nil, accessToken: String) {
|
|
||||||
self.id = UserAccountInfo.id(instanceURL: instanceURL, username: username)
|
|
||||||
self.instanceURL = instanceURL
|
|
||||||
self.clientID = clientID
|
|
||||||
self.clientSecret = clientSecret
|
|
||||||
self.username = username
|
|
||||||
self.accessToken = accessToken
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate init?(userDefaultsDict dict: [String: String]) {
|
|
||||||
guard let id = dict["id"],
|
|
||||||
let instanceURL = dict["instanceURL"],
|
|
||||||
let url = URL(string: instanceURL),
|
|
||||||
let clientID = dict["clientID"],
|
|
||||||
let secret = dict["clientSecret"],
|
|
||||||
let accessToken = dict["accessToken"] else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
self.id = id
|
|
||||||
self.instanceURL = url
|
|
||||||
self.clientID = clientID
|
|
||||||
self.clientSecret = secret
|
|
||||||
self.username = dict["username"]
|
|
||||||
self.accessToken = accessToken
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A filename-safe string for this account
|
|
||||||
var persistenceKey: String {
|
|
||||||
// slashes are not allowed in the persistent store coordinator name
|
|
||||||
id.replacingOccurrences(of: "/", with: "_")
|
|
||||||
}
|
|
||||||
|
|
||||||
func hash(into hasher: inout Hasher) {
|
|
||||||
hasher.combine(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
static func ==(lhs: UserAccountInfo, rhs: UserAccountInfo) -> Bool {
|
|
||||||
return lhs.id == rhs.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Notification.Name {
|
|
||||||
static let userLoggedOut = Notification.Name("Tusker.userLoggedOut")
|
static let userLoggedOut = Notification.Name("Tusker.userLoggedOut")
|
||||||
static let addAccount = Notification.Name("Tusker.addAccount")
|
static let addAccount = Notification.Name("Tusker.addAccount")
|
||||||
static let activateAccount = Notification.Name("Tusker.activateAccount")
|
static let activateAccount = Notification.Name("Tusker.activateAccount")
|
|
@ -142,7 +142,6 @@
|
||||||
D64BC18623C1253A000D0238 /* AssetPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64BC18523C1253A000D0238 /* AssetPreviewViewController.swift */; };
|
D64BC18623C1253A000D0238 /* AssetPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64BC18523C1253A000D0238 /* AssetPreviewViewController.swift */; };
|
||||||
D64BC18F23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64BC18D23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift */; };
|
D64BC18F23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64BC18D23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift */; };
|
||||||
D64BC19023C18B9D000D0238 /* FollowRequestNotificationTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D64BC18E23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.xib */; };
|
D64BC19023C18B9D000D0238 /* FollowRequestNotificationTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D64BC18E23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.xib */; };
|
||||||
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AAC2128D88B005A6F37 /* LocalData.swift */; };
|
|
||||||
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */; };
|
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */; };
|
||||||
D64D8CA92463B494006B0BAA /* MultiThreadDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */; };
|
D64D8CA92463B494006B0BAA /* MultiThreadDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */; };
|
||||||
D651C5B42915B00400236EF6 /* ProfileFieldsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D651C5B32915B00400236EF6 /* ProfileFieldsView.swift */; };
|
D651C5B42915B00400236EF6 /* ProfileFieldsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D651C5B32915B00400236EF6 /* ProfileFieldsView.swift */; };
|
||||||
|
@ -268,6 +267,7 @@
|
||||||
D6ADB6F028ED1F25009924AB /* CachedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6ADB6EF28ED1F25009924AB /* CachedImageView.swift */; };
|
D6ADB6F028ED1F25009924AB /* CachedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6ADB6EF28ED1F25009924AB /* CachedImageView.swift */; };
|
||||||
D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */; };
|
D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */; };
|
||||||
D6AEBB432321685E00E5038B /* OpenInSafariActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */; };
|
D6AEBB432321685E00E5038B /* OpenInSafariActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */; };
|
||||||
|
D6B0026E29B5248800C70BE2 /* UserAccounts in Frameworks */ = {isa = PBXBuildFile; productRef = D6B0026D29B5248800C70BE2 /* UserAccounts */; };
|
||||||
D6B053A223BD2C0600A066FA /* AssetPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B053A123BD2C0600A066FA /* AssetPickerViewController.swift */; };
|
D6B053A223BD2C0600A066FA /* AssetPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B053A123BD2C0600A066FA /* AssetPickerViewController.swift */; };
|
||||||
D6B053A423BD2C8100A066FA /* AssetCollectionsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B053A323BD2C8100A066FA /* AssetCollectionsListViewController.swift */; };
|
D6B053A423BD2C8100A066FA /* AssetCollectionsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B053A323BD2C8100A066FA /* AssetCollectionsListViewController.swift */; };
|
||||||
D6B053A623BD2D0C00A066FA /* AssetCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B053A523BD2D0C00A066FA /* AssetCollectionViewController.swift */; };
|
D6B053A623BD2D0C00A066FA /* AssetCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B053A523BD2D0C00A066FA /* AssetCollectionViewController.swift */; };
|
||||||
|
@ -559,7 +559,6 @@
|
||||||
D64BC18523C1253A000D0238 /* AssetPreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetPreviewViewController.swift; sourceTree = "<group>"; };
|
D64BC18523C1253A000D0238 /* AssetPreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetPreviewViewController.swift; sourceTree = "<group>"; };
|
||||||
D64BC18D23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowRequestNotificationTableViewCell.swift; sourceTree = "<group>"; };
|
D64BC18D23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowRequestNotificationTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
D64BC18E23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FollowRequestNotificationTableViewCell.xib; sourceTree = "<group>"; };
|
D64BC18E23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FollowRequestNotificationTableViewCell.xib; sourceTree = "<group>"; };
|
||||||
D64D0AAC2128D88B005A6F37 /* LocalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalData.swift; sourceTree = "<group>"; };
|
|
||||||
D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = "<group>"; };
|
D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = "<group>"; };
|
||||||
D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiThreadDictionary.swift; sourceTree = "<group>"; };
|
D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiThreadDictionary.swift; sourceTree = "<group>"; };
|
||||||
D651C5B32915B00400236EF6 /* ProfileFieldsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileFieldsView.swift; sourceTree = "<group>"; };
|
D651C5B32915B00400236EF6 /* ProfileFieldsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileFieldsView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -688,6 +687,7 @@
|
||||||
D6ADB6EF28ED1F25009924AB /* CachedImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachedImageView.swift; sourceTree = "<group>"; };
|
D6ADB6EF28ED1F25009924AB /* CachedImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachedImageView.swift; sourceTree = "<group>"; };
|
||||||
D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIActivity+Types.swift"; sourceTree = "<group>"; };
|
D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIActivity+Types.swift"; sourceTree = "<group>"; };
|
||||||
D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInSafariActivity.swift; sourceTree = "<group>"; };
|
D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInSafariActivity.swift; sourceTree = "<group>"; };
|
||||||
|
D6B0026C29B5245400C70BE2 /* UserAccounts */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = UserAccounts; path = Packages/UserAccounts; sourceTree = "<group>"; };
|
||||||
D6B053A123BD2C0600A066FA /* AssetPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetPickerViewController.swift; sourceTree = "<group>"; };
|
D6B053A123BD2C0600A066FA /* AssetPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetPickerViewController.swift; sourceTree = "<group>"; };
|
||||||
D6B053A323BD2C8100A066FA /* AssetCollectionsListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetCollectionsListViewController.swift; sourceTree = "<group>"; };
|
D6B053A323BD2C8100A066FA /* AssetCollectionsListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetCollectionsListViewController.swift; sourceTree = "<group>"; };
|
||||||
D6B053A523BD2D0C00A066FA /* AssetCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetCollectionViewController.swift; sourceTree = "<group>"; };
|
D6B053A523BD2D0C00A066FA /* AssetCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetCollectionViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -813,6 +813,7 @@
|
||||||
files = (
|
files = (
|
||||||
D674A50927F9128D00BA03AC /* Pachyderm in Frameworks */,
|
D674A50927F9128D00BA03AC /* Pachyderm in Frameworks */,
|
||||||
D659F35E2953A212002D944A /* TTTKit in Frameworks */,
|
D659F35E2953A212002D944A /* TTTKit in Frameworks */,
|
||||||
|
D6B0026E29B5248800C70BE2 /* UserAccounts in Frameworks */,
|
||||||
D60CFFDB24A290BA00D00083 /* SwiftSoup in Frameworks */,
|
D60CFFDB24A290BA00D00083 /* SwiftSoup in Frameworks */,
|
||||||
D60088EF2980D8B5005B4D00 /* StoreKit.framework in Frameworks */,
|
D60088EF2980D8B5005B4D00 /* StoreKit.framework in Frameworks */,
|
||||||
D6552367289870790048A653 /* ScreenCorners in Frameworks */,
|
D6552367289870790048A653 /* ScreenCorners in Frameworks */,
|
||||||
|
@ -1537,6 +1538,7 @@
|
||||||
D674A50727F910F300BA03AC /* Pachyderm */,
|
D674A50727F910F300BA03AC /* Pachyderm */,
|
||||||
D6BEA243291A0C83002F4D01 /* Duckable */,
|
D6BEA243291A0C83002F4D01 /* Duckable */,
|
||||||
D68A76F22953915C001DA1B3 /* TTTKit */,
|
D68A76F22953915C001DA1B3 /* TTTKit */,
|
||||||
|
D6B0026C29B5245400C70BE2 /* UserAccounts */,
|
||||||
D6D4DDCE212518A000E1C4BB /* Tusker */,
|
D6D4DDCE212518A000E1C4BB /* Tusker */,
|
||||||
D6D4DDE3212518A200E1C4BB /* TuskerTests */,
|
D6D4DDE3212518A200E1C4BB /* TuskerTests */,
|
||||||
D6D4DDEE212518A200E1C4BB /* TuskerUITests */,
|
D6D4DDEE212518A200E1C4BB /* TuskerUITests */,
|
||||||
|
@ -1573,7 +1575,6 @@
|
||||||
D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */,
|
D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */,
|
||||||
D61F75BC293D099600C0B37F /* Lazy.swift */,
|
D61F75BC293D099600C0B37F /* Lazy.swift */,
|
||||||
D60E2F2B24423EAD005F8713 /* LazilyDecoding.swift */,
|
D60E2F2B24423EAD005F8713 /* LazilyDecoding.swift */,
|
||||||
D64D0AAC2128D88B005A6F37 /* LocalData.swift */,
|
|
||||||
D61DC84528F498F200B82C6E /* Logging.swift */,
|
D61DC84528F498F200B82C6E /* Logging.swift */,
|
||||||
D6B81F432560390300F6E31D /* MenuController.swift */,
|
D6B81F432560390300F6E31D /* MenuController.swift */,
|
||||||
D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */,
|
D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */,
|
||||||
|
@ -1741,6 +1742,7 @@
|
||||||
D63CC701290EC0B8000E19DE /* Sentry */,
|
D63CC701290EC0B8000E19DE /* Sentry */,
|
||||||
D6BEA244291A0EDE002F4D01 /* Duckable */,
|
D6BEA244291A0EDE002F4D01 /* Duckable */,
|
||||||
D659F35D2953A212002D944A /* TTTKit */,
|
D659F35D2953A212002D944A /* TTTKit */,
|
||||||
|
D6B0026D29B5248800C70BE2 /* UserAccounts */,
|
||||||
);
|
);
|
||||||
productName = Tusker;
|
productName = Tusker;
|
||||||
productReference = D6D4DDCC212518A000E1C4BB /* Tusker.app */;
|
productReference = D6D4DDCC212518A000E1C4BB /* Tusker.app */;
|
||||||
|
@ -2147,7 +2149,6 @@
|
||||||
D6DD2A45273D6C5700386A6C /* GIFImageView.swift in Sources */,
|
D6DD2A45273D6C5700386A6C /* GIFImageView.swift in Sources */,
|
||||||
D61F759029353B4300C0B37F /* FileManager+Size.swift in Sources */,
|
D61F759029353B4300C0B37F /* FileManager+Size.swift in Sources */,
|
||||||
0427033822B30F5F000D31B6 /* BehaviorPrefsView.swift in Sources */,
|
0427033822B30F5F000D31B6 /* BehaviorPrefsView.swift in Sources */,
|
||||||
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */,
|
|
||||||
D6945C2F23AC47C3005C403C /* SavedDataManager.swift in Sources */,
|
D6945C2F23AC47C3005C403C /* SavedDataManager.swift in Sources */,
|
||||||
D6C94D892139E6EC00CB5196 /* AttachmentView.swift in Sources */,
|
D6C94D892139E6EC00CB5196 /* AttachmentView.swift in Sources */,
|
||||||
D6C693EF216192C2007D6A6D /* TuskerNavigationDelegate.swift in Sources */,
|
D6C693EF216192C2007D6A6D /* TuskerNavigationDelegate.swift in Sources */,
|
||||||
|
@ -2966,6 +2967,10 @@
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = Pachyderm;
|
productName = Pachyderm;
|
||||||
};
|
};
|
||||||
|
D6B0026D29B5248800C70BE2 /* UserAccounts */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = UserAccounts;
|
||||||
|
};
|
||||||
D6BEA244291A0EDE002F4D01 /* Duckable */ = {
|
D6BEA244291A0EDE002F4D01 /* Duckable */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = Duckable;
|
productName = Duckable;
|
||||||
|
|
|
@ -7,13 +7,14 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
class LogoutService {
|
class LogoutService {
|
||||||
let accountInfo: LocalData.UserAccountInfo
|
let accountInfo: UserAccountInfo
|
||||||
private let mastodonController: MastodonController
|
private let mastodonController: MastodonController
|
||||||
|
|
||||||
init(accountInfo: LocalData.UserAccountInfo) {
|
init(accountInfo: UserAccountInfo) {
|
||||||
self.accountInfo = accountInfo
|
self.accountInfo = accountInfo
|
||||||
self.mastodonController = MastodonController.getForAccount(accountInfo)
|
self.mastodonController = MastodonController.getForAccount(accountInfo)
|
||||||
}
|
}
|
||||||
|
@ -23,7 +24,7 @@ class LogoutService {
|
||||||
try? await self.mastodonController.client.revokeAccessToken()
|
try? await self.mastodonController.client.revokeAccessToken()
|
||||||
}
|
}
|
||||||
MastodonController.removeForAccount(accountInfo)
|
MastodonController.removeForAccount(accountInfo)
|
||||||
LocalData.shared.removeAccount(accountInfo)
|
UserAccountsManager.shared.removeAccount(accountInfo)
|
||||||
let psc = mastodonController.persistentContainer.persistentStoreCoordinator
|
let psc = mastodonController.persistentContainer.persistentStoreCoordinator
|
||||||
for store in psc.persistentStores {
|
for store in psc.persistentStores {
|
||||||
guard let url = store.url else {
|
guard let url = store.url else {
|
||||||
|
|
|
@ -9,15 +9,16 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import Combine
|
import Combine
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
class MastodonController: ObservableObject {
|
class MastodonController: ObservableObject {
|
||||||
|
|
||||||
static private(set) var all = [LocalData.UserAccountInfo: MastodonController]()
|
static private(set) var all = [UserAccountInfo: MastodonController]()
|
||||||
|
|
||||||
@available(*, message: "do something less dumb")
|
@available(*, message: "do something less dumb")
|
||||||
static var first: MastodonController { all.first!.value }
|
static var first: MastodonController { all.first!.value }
|
||||||
|
|
||||||
static func getForAccount(_ account: LocalData.UserAccountInfo) -> MastodonController {
|
static func getForAccount(_ account: UserAccountInfo) -> MastodonController {
|
||||||
if let controller = all[account] {
|
if let controller = all[account] {
|
||||||
return controller
|
return controller
|
||||||
} else {
|
} else {
|
||||||
|
@ -31,7 +32,7 @@ class MastodonController: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func removeForAccount(_ account: LocalData.UserAccountInfo) {
|
static func removeForAccount(_ account: UserAccountInfo) {
|
||||||
all.removeValue(forKey: account)
|
all.removeValue(forKey: account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +44,7 @@ class MastodonController: ObservableObject {
|
||||||
private(set) nonisolated lazy var persistentContainer = MastodonCachePersistentStore(for: accountInfo, transient: transient)
|
private(set) nonisolated lazy var persistentContainer = MastodonCachePersistentStore(for: accountInfo, transient: transient)
|
||||||
|
|
||||||
let instanceURL: URL
|
let instanceURL: URL
|
||||||
var accountInfo: LocalData.UserAccountInfo?
|
var accountInfo: UserAccountInfo?
|
||||||
var accountPreferences: AccountPreferences!
|
var accountPreferences: AccountPreferences!
|
||||||
|
|
||||||
let client: Client!
|
let client: Client!
|
||||||
|
|
|
@ -10,6 +10,7 @@ import UIKit
|
||||||
import CoreData
|
import CoreData
|
||||||
import OSLog
|
import OSLog
|
||||||
import Sentry
|
import Sentry
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
let stateRestorationLogger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "StateRestoration")
|
let stateRestorationLogger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "StateRestoration")
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
if let oldSavedData = SavedDataManager.load() {
|
if let oldSavedData = SavedDataManager.load() {
|
||||||
do {
|
do {
|
||||||
for account in oldSavedData.accountIDs {
|
for account in oldSavedData.accountIDs {
|
||||||
guard let account = LocalData.shared.getAccount(id: account) else {
|
guard let account = UserAccountsManager.shared.getAccount(id: account) else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let controller = MastodonController.getForAccount(account)
|
let controller = MastodonController.getForAccount(account)
|
||||||
|
|
|
@ -9,11 +9,12 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import CoreData
|
import CoreData
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
@objc(AccountPreferences)
|
@objc(AccountPreferences)
|
||||||
public final class AccountPreferences: NSManagedObject {
|
public final class AccountPreferences: NSManagedObject {
|
||||||
|
|
||||||
@nonobjc class func fetchRequest(account: LocalData.UserAccountInfo) -> NSFetchRequest<AccountPreferences> {
|
@nonobjc class func fetchRequest(account: UserAccountInfo) -> NSFetchRequest<AccountPreferences> {
|
||||||
let req = NSFetchRequest<AccountPreferences>(entityName: "AccountPreferences")
|
let req = NSFetchRequest<AccountPreferences>(entityName: "AccountPreferences")
|
||||||
req.predicate = NSPredicate(format: "accountID = %@", account.id)
|
req.predicate = NSPredicate(format: "accountID = %@", account.id)
|
||||||
req.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: true)]
|
req.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: true)]
|
||||||
|
@ -27,7 +28,7 @@ public final class AccountPreferences: NSManagedObject {
|
||||||
@LazilyDecoding(from: \AccountPreferences.pinnedTimelinesData, fallback: AccountPreferences.defaultPinnedTimelines)
|
@LazilyDecoding(from: \AccountPreferences.pinnedTimelinesData, fallback: AccountPreferences.defaultPinnedTimelines)
|
||||||
var pinnedTimelines: [PinnedTimeline]
|
var pinnedTimelines: [PinnedTimeline]
|
||||||
|
|
||||||
static func `default`(account: LocalData.UserAccountInfo, context: NSManagedObjectContext) -> AccountPreferences {
|
static func `default`(account: UserAccountInfo, context: NSManagedObjectContext) -> AccountPreferences {
|
||||||
let prefs = AccountPreferences(context: context)
|
let prefs = AccountPreferences(context: context)
|
||||||
prefs.accountID = account.id
|
prefs.accountID = account.id
|
||||||
prefs.createdAt = Date()
|
prefs.createdAt = Date()
|
||||||
|
|
|
@ -13,12 +13,13 @@ import Combine
|
||||||
import OSLog
|
import OSLog
|
||||||
import Sentry
|
import Sentry
|
||||||
import CloudKit
|
import CloudKit
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
fileprivate let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "PersistentStore")
|
fileprivate let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "PersistentStore")
|
||||||
|
|
||||||
class MastodonCachePersistentStore: NSPersistentCloudKitContainer {
|
class MastodonCachePersistentStore: NSPersistentCloudKitContainer {
|
||||||
|
|
||||||
private let accountInfo: LocalData.UserAccountInfo?
|
private let accountInfo: UserAccountInfo?
|
||||||
|
|
||||||
private static let managedObjectModel: NSManagedObjectModel = {
|
private static let managedObjectModel: NSManagedObjectModel = {
|
||||||
let url = Bundle.main.url(forResource: "Tusker", withExtension: "momd")!
|
let url = Bundle.main.url(forResource: "Tusker", withExtension: "momd")!
|
||||||
|
@ -51,7 +52,7 @@ class MastodonCachePersistentStore: NSPersistentCloudKitContainer {
|
||||||
let accountSubject = PassthroughSubject<String, Never>()
|
let accountSubject = PassthroughSubject<String, Never>()
|
||||||
let relationshipSubject = PassthroughSubject<String, Never>()
|
let relationshipSubject = PassthroughSubject<String, Never>()
|
||||||
|
|
||||||
init(for accountInfo: LocalData.UserAccountInfo?, transient: Bool = false) {
|
init(for accountInfo: UserAccountInfo?, transient: Bool = false) {
|
||||||
self.accountInfo = accountInfo
|
self.accountInfo = accountInfo
|
||||||
|
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
|
|
|
@ -10,6 +10,7 @@ import Foundation
|
||||||
import CoreData
|
import CoreData
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import WebURLFoundationExtras
|
import WebURLFoundationExtras
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
@objc(SavedHashtag)
|
@objc(SavedHashtag)
|
||||||
public final class SavedHashtag: NSManagedObject {
|
public final class SavedHashtag: NSManagedObject {
|
||||||
|
@ -18,13 +19,13 @@ public final class SavedHashtag: NSManagedObject {
|
||||||
return NSFetchRequest<SavedHashtag>(entityName: "SavedHashtag")
|
return NSFetchRequest<SavedHashtag>(entityName: "SavedHashtag")
|
||||||
}
|
}
|
||||||
|
|
||||||
@nonobjc class func fetchRequest(account: LocalData.UserAccountInfo) -> NSFetchRequest<SavedHashtag> {
|
@nonobjc class func fetchRequest(account: UserAccountInfo) -> NSFetchRequest<SavedHashtag> {
|
||||||
let req = NSFetchRequest<SavedHashtag>(entityName: "SavedHashtag")
|
let req = NSFetchRequest<SavedHashtag>(entityName: "SavedHashtag")
|
||||||
req.predicate = NSPredicate(format: "accountID = %@", account.id)
|
req.predicate = NSPredicate(format: "accountID = %@", account.id)
|
||||||
return req
|
return req
|
||||||
}
|
}
|
||||||
|
|
||||||
@nonobjc class func fetchRequest(name: String, account: LocalData.UserAccountInfo) -> NSFetchRequest<SavedHashtag> {
|
@nonobjc class func fetchRequest(name: String, account: UserAccountInfo) -> NSFetchRequest<SavedHashtag> {
|
||||||
let req = NSFetchRequest<SavedHashtag>(entityName: "SavedHashtag")
|
let req = NSFetchRequest<SavedHashtag>(entityName: "SavedHashtag")
|
||||||
req.predicate = NSPredicate(format: "name LIKE[cd] %@ AND accountID = %@", name, account.id)
|
req.predicate = NSPredicate(format: "name LIKE[cd] %@ AND accountID = %@", name, account.id)
|
||||||
return req
|
return req
|
||||||
|
@ -37,7 +38,7 @@ public final class SavedHashtag: NSManagedObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SavedHashtag {
|
extension SavedHashtag {
|
||||||
convenience init(hashtag: Hashtag, account: LocalData.UserAccountInfo, context: NSManagedObjectContext) {
|
convenience init(hashtag: Hashtag, account: UserAccountInfo, context: NSManagedObjectContext) {
|
||||||
self.init(context: context)
|
self.init(context: context)
|
||||||
self.accountID = account.id
|
self.accountID = account.id
|
||||||
self.name = hashtag.name
|
self.name = hashtag.name
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import CoreData
|
import CoreData
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
@objc(SavedInstance)
|
@objc(SavedInstance)
|
||||||
public final class SavedInstance: NSManagedObject {
|
public final class SavedInstance: NSManagedObject {
|
||||||
|
@ -16,13 +17,13 @@ public final class SavedInstance: NSManagedObject {
|
||||||
return NSFetchRequest<SavedInstance>(entityName: "SavedInstance")
|
return NSFetchRequest<SavedInstance>(entityName: "SavedInstance")
|
||||||
}
|
}
|
||||||
|
|
||||||
@nonobjc class func fetchRequest(account: LocalData.UserAccountInfo) -> NSFetchRequest<SavedInstance> {
|
@nonobjc class func fetchRequest(account: UserAccountInfo) -> NSFetchRequest<SavedInstance> {
|
||||||
let req = NSFetchRequest<SavedInstance>(entityName: "SavedInstance")
|
let req = NSFetchRequest<SavedInstance>(entityName: "SavedInstance")
|
||||||
req.predicate = NSPredicate(format: "accountID = %@", account.id)
|
req.predicate = NSPredicate(format: "accountID = %@", account.id)
|
||||||
return req
|
return req
|
||||||
}
|
}
|
||||||
|
|
||||||
@nonobjc class func fetchRequest(url: URL, account: LocalData.UserAccountInfo) -> NSFetchRequest<SavedInstance> {
|
@nonobjc class func fetchRequest(url: URL, account: UserAccountInfo) -> NSFetchRequest<SavedInstance> {
|
||||||
let req = NSFetchRequest<SavedInstance>(entityName: "SavedInstance")
|
let req = NSFetchRequest<SavedInstance>(entityName: "SavedInstance")
|
||||||
req.predicate = NSPredicate(format: "url = %@ AND accountID = %@", url as NSURL, account.id)
|
req.predicate = NSPredicate(format: "url = %@ AND accountID = %@", url as NSURL, account.id)
|
||||||
return req
|
return req
|
||||||
|
@ -34,7 +35,7 @@ public final class SavedInstance: NSManagedObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SavedInstance {
|
extension SavedInstance {
|
||||||
convenience init(url: URL, account: LocalData.UserAccountInfo, context: NSManagedObjectContext) {
|
convenience init(url: URL, account: UserAccountInfo, context: NSManagedObjectContext) {
|
||||||
self.init(context: context)
|
self.init(context: context)
|
||||||
self.accountID = account.id
|
self.accountID = account.id
|
||||||
self.url = url
|
self.url = url
|
||||||
|
|
|
@ -9,11 +9,12 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import CoreData
|
import CoreData
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
@objc(TimelinePosition)
|
@objc(TimelinePosition)
|
||||||
public final class TimelinePosition: NSManagedObject {
|
public final class TimelinePosition: NSManagedObject {
|
||||||
|
|
||||||
@nonobjc class func fetchRequest(timeline: Timeline, account: LocalData.UserAccountInfo) -> NSFetchRequest<TimelinePosition> {
|
@nonobjc class func fetchRequest(timeline: Timeline, account: UserAccountInfo) -> NSFetchRequest<TimelinePosition> {
|
||||||
let req = NSFetchRequest<TimelinePosition>(entityName: "TimelinePosition")
|
let req = NSFetchRequest<TimelinePosition>(entityName: "TimelinePosition")
|
||||||
req.predicate = NSPredicate(format: "accountID = %@ AND timelineKind = %@", account.id, toTimelineKind(timeline))
|
req.predicate = NSPredicate(format: "accountID = %@ AND timelineKind = %@", account.id, toTimelineKind(timeline))
|
||||||
req.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: true)]
|
req.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: true)]
|
||||||
|
@ -34,7 +35,7 @@ public final class TimelinePosition: NSManagedObject {
|
||||||
set { timelineKind = toTimelineKind(newValue) }
|
set { timelineKind = toTimelineKind(newValue) }
|
||||||
}
|
}
|
||||||
|
|
||||||
convenience init(timeline: Timeline, account: LocalData.UserAccountInfo, context: NSManagedObjectContext) {
|
convenience init(timeline: Timeline, account: UserAccountInfo, context: NSManagedObjectContext) {
|
||||||
self.init(context: context)
|
self.init(context: context)
|
||||||
self.timeline = timeline
|
self.timeline = timeline
|
||||||
self.accountID = account.id
|
self.accountID = account.id
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
class AuxiliarySceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate {
|
class AuxiliarySceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate {
|
||||||
|
|
||||||
|
@ -31,11 +32,11 @@ class AuxiliarySceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDel
|
||||||
}
|
}
|
||||||
launchActivity = activity
|
launchActivity = activity
|
||||||
|
|
||||||
let account: LocalData.UserAccountInfo
|
let account: UserAccountInfo
|
||||||
|
|
||||||
if let activityAccount = UserActivityManager.getAccount(from: activity) {
|
if let activityAccount = UserActivityManager.getAccount(from: activity) {
|
||||||
account = activityAccount
|
account = activityAccount
|
||||||
} else if let mostRecent = LocalData.shared.getMostRecentAccount() {
|
} else if let mostRecent = UserAccountsManager.shared.getMostRecentAccount() {
|
||||||
account = mostRecent
|
account = mostRecent
|
||||||
} else {
|
} else {
|
||||||
// without an account, we can't do anything so we just destroy the scene
|
// without an account, we can't do anything so we just destroy the scene
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
class ComposeSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate {
|
class ComposeSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate {
|
||||||
|
|
||||||
|
@ -22,12 +23,12 @@ class ComposeSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDeleg
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard LocalData.shared.onboardingComplete else {
|
guard UserAccountsManager.shared.onboardingComplete else {
|
||||||
UIApplication.shared.requestSceneSessionDestruction(session, options: nil, errorHandler: nil)
|
UIApplication.shared.requestSceneSessionDestruction(session, options: nil, errorHandler: nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let account: LocalData.UserAccountInfo
|
let account: UserAccountInfo
|
||||||
let controller: MastodonController
|
let controller: MastodonController
|
||||||
let draft: Draft?
|
let draft: Draft?
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ class ComposeSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDeleg
|
||||||
account = activityAccount
|
account = activityAccount
|
||||||
} else {
|
} else {
|
||||||
// todo: this potentially changes the account for the draft, should show the same warning to user as in the drafts selection screen
|
// todo: this potentially changes the account for the draft, should show the same warning to user as in the drafts selection screen
|
||||||
account = LocalData.shared.getMostRecentAccount()!
|
account = UserAccountsManager.shared.getMostRecentAccount()!
|
||||||
}
|
}
|
||||||
|
|
||||||
controller = MastodonController.getForAccount(account)
|
controller = MastodonController.getForAccount(account)
|
||||||
|
@ -49,7 +50,7 @@ class ComposeSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDeleg
|
||||||
draft = nil
|
draft = nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
account = LocalData.shared.getMostRecentAccount()!
|
account = UserAccountsManager.shared.getMostRecentAccount()!
|
||||||
controller = MastodonController.getForAccount(account)
|
controller = MastodonController.getForAccount(account)
|
||||||
draft = nil
|
draft = nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import Pachyderm
|
||||||
import MessageUI
|
import MessageUI
|
||||||
import CoreData
|
import CoreData
|
||||||
import Duckable
|
import Duckable
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
class MainSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate {
|
class MainSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate {
|
||||||
|
|
||||||
|
@ -161,13 +162,13 @@ class MainSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate
|
||||||
|
|
||||||
func showAppOrOnboardingUI(session: UISceneSession? = nil) {
|
func showAppOrOnboardingUI(session: UISceneSession? = nil) {
|
||||||
let session = session ?? window!.windowScene!.session
|
let session = session ?? window!.windowScene!.session
|
||||||
if LocalData.shared.onboardingComplete {
|
if UserAccountsManager.shared.onboardingComplete {
|
||||||
let account: LocalData.UserAccountInfo
|
let account: UserAccountInfo
|
||||||
if let activity = launchActivity,
|
if let activity = launchActivity,
|
||||||
let activityAccount = UserActivityManager.getAccount(from: activity) {
|
let activityAccount = UserActivityManager.getAccount(from: activity) {
|
||||||
account = activityAccount
|
account = activityAccount
|
||||||
} else {
|
} else {
|
||||||
account = LocalData.shared.getMostRecentAccount()!
|
account = UserAccountsManager.shared.getMostRecentAccount()!
|
||||||
}
|
}
|
||||||
|
|
||||||
if session.mastodonController == nil {
|
if session.mastodonController == nil {
|
||||||
|
@ -194,9 +195,9 @@ class MainSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func activateAccount(_ account: LocalData.UserAccountInfo, animated: Bool) {
|
func activateAccount(_ account: UserAccountInfo, animated: Bool) {
|
||||||
let oldMostRecentAccount = LocalData.shared.mostRecentAccountID
|
let oldMostRecentAccount = UserAccountsManager.shared.mostRecentAccountID
|
||||||
LocalData.shared.setMostRecentAccount(account)
|
UserAccountsManager.shared.setMostRecentAccount(account)
|
||||||
window!.windowScene!.session.mastodonController = MastodonController.getForAccount(account)
|
window!.windowScene!.session.mastodonController = MastodonController.getForAccount(account)
|
||||||
|
|
||||||
// iPadOS shows the title below the App Name
|
// iPadOS shows the title below the App Name
|
||||||
|
@ -212,8 +213,8 @@ class MainSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate
|
||||||
if let container = window?.rootViewController as? AccountSwitchingContainerViewController {
|
if let container = window?.rootViewController as? AccountSwitchingContainerViewController {
|
||||||
let direction: AccountSwitchingContainerViewController.AnimationDirection
|
let direction: AccountSwitchingContainerViewController.AnimationDirection
|
||||||
if animated,
|
if animated,
|
||||||
let oldIndex = LocalData.shared.accounts.firstIndex(where: { $0.id == oldMostRecentAccount }),
|
let oldIndex = UserAccountsManager.shared.accounts.firstIndex(where: { $0.id == oldMostRecentAccount }),
|
||||||
let newIndex = LocalData.shared.accounts.firstIndex(of: account) {
|
let newIndex = UserAccountsManager.shared.accounts.firstIndex(of: account) {
|
||||||
direction = newIndex > oldIndex ? .upwards : .downwards
|
direction = newIndex > oldIndex ? .upwards : .downwards
|
||||||
} else {
|
} else {
|
||||||
direction = .none
|
direction = .none
|
||||||
|
@ -229,8 +230,8 @@ class MainSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
LogoutService(accountInfo: account).run()
|
LogoutService(accountInfo: account).run()
|
||||||
if LocalData.shared.onboardingComplete {
|
if UserAccountsManager.shared.onboardingComplete {
|
||||||
activateAccount(LocalData.shared.accounts.first!, animated: false)
|
activateAccount(UserAccountsManager.shared.accounts.first!, animated: false)
|
||||||
} else {
|
} else {
|
||||||
window!.rootViewController = createOnboardingUI()
|
window!.rootViewController = createOnboardingUI()
|
||||||
}
|
}
|
||||||
|
@ -269,7 +270,7 @@ class MainSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MainSceneDelegate: OnboardingViewControllerDelegate {
|
extension MainSceneDelegate: OnboardingViewControllerDelegate {
|
||||||
func didFinishOnboarding(account: LocalData.UserAccountInfo) {
|
func didFinishOnboarding(account: UserAccountInfo) {
|
||||||
activateAccount(account, animated: false)
|
activateAccount(account, animated: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
protocol FastAccountSwitcherViewControllerDelegate: AnyObject {
|
protocol FastAccountSwitcherViewControllerDelegate: AnyObject {
|
||||||
func fastAccountSwitcherAddToViewHierarchy(_ fastAccountSwitcher: FastAccountSwitcherViewController)
|
func fastAccountSwitcherAddToViewHierarchy(_ fastAccountSwitcher: FastAccountSwitcherViewController)
|
||||||
|
@ -139,9 +140,9 @@ class FastAccountSwitcherViewController: UIViewController {
|
||||||
addAccountPlaceholder
|
addAccountPlaceholder
|
||||||
]
|
]
|
||||||
|
|
||||||
for account in LocalData.shared.accounts {
|
for account in UserAccountsManager.shared.accounts {
|
||||||
let accountView = FastSwitchingAccountView(account: account, orientation: itemOrientation)
|
let accountView = FastSwitchingAccountView(account: account, orientation: itemOrientation)
|
||||||
accountView.isCurrent = account.id == LocalData.shared.mostRecentAccountID
|
accountView.isCurrent = account.id == UserAccountsManager.shared.mostRecentAccountID
|
||||||
accountsStack.addArrangedSubview(accountView)
|
accountsStack.addArrangedSubview(accountView)
|
||||||
accountViews.append(accountView)
|
accountViews.append(accountView)
|
||||||
}
|
}
|
||||||
|
@ -168,9 +169,9 @@ class FastAccountSwitcherViewController: UIViewController {
|
||||||
(self.view.window!.windowScene!.delegate as! MainSceneDelegate).showAddAccount()
|
(self.view.window!.windowScene!.delegate as! MainSceneDelegate).showAddAccount()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let account = LocalData.shared.accounts[newIndex - 1]
|
let account = UserAccountsManager.shared.accounts[newIndex - 1]
|
||||||
|
|
||||||
if account.id != LocalData.shared.mostRecentAccountID {
|
if account.id != UserAccountsManager.shared.mostRecentAccountID {
|
||||||
if hapticFeedback {
|
if hapticFeedback {
|
||||||
selectionChangedFeedbackGenerator?.selectionChanged()
|
selectionChangedFeedbackGenerator?.selectionChanged()
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
class FastSwitchingAccountView: UIView {
|
class FastSwitchingAccountView: UIView {
|
||||||
|
|
||||||
|
@ -49,7 +50,7 @@ class FastSwitchingAccountView: UIView {
|
||||||
|
|
||||||
private var avatarRequest: ImageCache.Request?
|
private var avatarRequest: ImageCache.Request?
|
||||||
|
|
||||||
init(account: LocalData.UserAccountInfo, orientation: FastAccountSwitcherViewController.ItemOrientation) {
|
init(account: UserAccountInfo, orientation: FastAccountSwitcherViewController.ItemOrientation) {
|
||||||
self.orientation = orientation
|
self.orientation = orientation
|
||||||
super.init(frame: .zero)
|
super.init(frame: .zero)
|
||||||
commonInit()
|
commonInit()
|
||||||
|
@ -121,7 +122,7 @@ class FastSwitchingAccountView: UIView {
|
||||||
isAccessibilityElement = true
|
isAccessibilityElement = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupAccount(account: LocalData.UserAccountInfo) {
|
private func setupAccount(account: UserAccountInfo) {
|
||||||
usernameLabel.text = account.username
|
usernameLabel.text = account.username
|
||||||
instanceLabel.text = account.instanceURL.host!
|
instanceLabel.text = account.instanceURL.host!
|
||||||
let controller = MastodonController.getForAccount(account)
|
let controller = MastodonController.getForAccount(account)
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import ScreenCorners
|
import ScreenCorners
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
class AccountSwitchingContainerViewController: UIViewController {
|
class AccountSwitchingContainerViewController: UIViewController {
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ class AccountSwitchingContainerViewController: UIViewController {
|
||||||
|
|
||||||
private var userActivities: [String: NSUserActivity] = [:]
|
private var userActivities: [String: NSUserActivity] = [:]
|
||||||
|
|
||||||
init(root: TuskerRootViewController, for account: LocalData.UserAccountInfo) {
|
init(root: TuskerRootViewController, for account: UserAccountInfo) {
|
||||||
self.currentAccountID = account.id
|
self.currentAccountID = account.id
|
||||||
self.root = root
|
self.root = root
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ class AccountSwitchingContainerViewController: UIViewController {
|
||||||
embedChild(root)
|
embedChild(root)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setRoot(_ newRoot: TuskerRootViewController, for account: LocalData.UserAccountInfo, animating direction: AnimationDirection) {
|
func setRoot(_ newRoot: TuskerRootViewController, for account: UserAccountInfo, animating direction: AnimationDirection) {
|
||||||
let oldRoot = self.root
|
let oldRoot = self.root
|
||||||
if direction == .none {
|
if direction == .none {
|
||||||
oldRoot.removeViewAndController()
|
oldRoot.removeViewAndController()
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
class MainSidebarMyProfileCollectionViewCell: UICollectionViewListCell {
|
class MainSidebarMyProfileCollectionViewCell: UICollectionViewListCell {
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ class MainSidebarMyProfileCollectionViewCell: UICollectionViewListCell {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUI(item: MainSidebarViewController.Item, account: LocalData.UserAccountInfo) async {
|
func updateUI(item: MainSidebarViewController.Item, account: UserAccountInfo) async {
|
||||||
var config = defaultContentConfiguration()
|
var config = defaultContentConfiguration()
|
||||||
config.text = item.title
|
config.text = item.title
|
||||||
config.image = UIImage(systemName: item.imageName!)
|
config.image = UIImage(systemName: item.imageName!)
|
||||||
|
|
|
@ -10,10 +10,11 @@ import UIKit
|
||||||
import AuthenticationServices
|
import AuthenticationServices
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import OSLog
|
import OSLog
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
protocol OnboardingViewControllerDelegate {
|
protocol OnboardingViewControllerDelegate {
|
||||||
@MainActor
|
@MainActor
|
||||||
func didFinishOnboarding(account: LocalData.UserAccountInfo)
|
func didFinishOnboarding(account: UserAccountInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "OnboardingViewController")
|
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "OnboardingViewController")
|
||||||
|
@ -145,7 +146,7 @@ class OnboardingViewController: UINavigationController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// construct a temporary UserAccountInfo instance for the MastodonController to use to fetch its own account
|
// construct a temporary UserAccountInfo instance for the MastodonController to use to fetch its own account
|
||||||
let tempAccountInfo = LocalData.UserAccountInfo(tempInstanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, accessToken: accessToken)
|
let tempAccountInfo = UserAccountInfo(tempInstanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, accessToken: accessToken)
|
||||||
mastodonController.accountInfo = tempAccountInfo
|
mastodonController.accountInfo = tempAccountInfo
|
||||||
|
|
||||||
updateStatus("Checking Credentials")
|
updateStatus("Checking Credentials")
|
||||||
|
@ -158,7 +159,7 @@ class OnboardingViewController: UINavigationController {
|
||||||
throw Error.gettingOwnAccount(error)
|
throw Error.gettingOwnAccount(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
let accountInfo = LocalData.shared.addAccount(instanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, username: ownAccount.username, accessToken: accessToken)
|
let accountInfo = UserAccountsManager.shared.addAccount(instanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, username: ownAccount.username, accessToken: accessToken)
|
||||||
mastodonController.accountInfo = accountInfo
|
mastodonController.accountInfo = accountInfo
|
||||||
|
|
||||||
self.onboardingDelegate?.didFinishOnboarding(account: accountInfo)
|
self.onboardingDelegate?.didFinishOnboarding(account: accountInfo)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import SwiftUI
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import CoreData
|
import CoreData
|
||||||
import CloudKit
|
import CloudKit
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
struct AdvancedPrefsView : View {
|
struct AdvancedPrefsView : View {
|
||||||
@ObservedObject var preferences = Preferences.shared
|
@ObservedObject var preferences = Preferences.shared
|
||||||
|
@ -30,7 +31,7 @@ struct AdvancedPrefsView : View {
|
||||||
|
|
||||||
var formattingFooter: some View {
|
var formattingFooter: some View {
|
||||||
var s: AttributedString = "This option is only supported with Pleroma and some compatible Mastodon instances (such as Glitch).\n"
|
var s: AttributedString = "This option is only supported with Pleroma and some compatible Mastodon instances (such as Glitch).\n"
|
||||||
if let account = LocalData.shared.getMostRecentAccount() {
|
if let account = UserAccountsManager.shared.getMostRecentAccount() {
|
||||||
let mastodonController = MastodonController.getForAccount(account)
|
let mastodonController = MastodonController.getForAccount(account)
|
||||||
// shouldn't need to load the instance here, because loading it is kicked off my the scene delegate
|
// shouldn't need to load the instance here, because loading it is kicked off my the scene delegate
|
||||||
if !mastodonController.instanceFeatures.probablySupportsMarkdown {
|
if !mastodonController.instanceFeatures.probablySupportsMarkdown {
|
||||||
|
@ -135,7 +136,7 @@ struct AdvancedPrefsView : View {
|
||||||
].map {
|
].map {
|
||||||
$0.getDiskSizeInBytes() ?? 0
|
$0.getDiskSizeInBytes() ?? 0
|
||||||
}.reduce(0, +)
|
}.reduce(0, +)
|
||||||
mastodonCacheSize = LocalData.shared.accounts.map {
|
mastodonCacheSize = UserAccountsManager.shared.accounts.map {
|
||||||
let descriptions = MastodonController.getForAccount($0).persistentContainer.persistentStoreDescriptions
|
let descriptions = MastodonController.getForAccount($0).persistentContainer.persistentStoreDescriptions
|
||||||
return descriptions.map {
|
return descriptions.map {
|
||||||
guard let url = $0.url else {
|
guard let url = $0.url else {
|
||||||
|
@ -148,7 +149,7 @@ struct AdvancedPrefsView : View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func clearCache() {
|
private func clearCache() {
|
||||||
for account in LocalData.shared.accounts {
|
for account in UserAccountsManager.shared.accounts {
|
||||||
let controller = MastodonController.getForAccount(account)
|
let controller = MastodonController.getForAccount(account)
|
||||||
let container = controller.persistentContainer
|
let container = controller.persistentContainer
|
||||||
do {
|
do {
|
||||||
|
@ -178,7 +179,7 @@ struct AdvancedPrefsView : View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func resetUI() {
|
private func resetUI() {
|
||||||
let mostRecent = LocalData.shared.getMostRecentAccount()!
|
let mostRecent = UserAccountsManager.shared.getMostRecentAccount()!
|
||||||
NotificationCenter.default.post(name: .activateAccount, object: nil, userInfo: ["account": mostRecent])
|
NotificationCenter.default.post(name: .activateAccount, object: nil, userInfo: ["account": mostRecent])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
struct LocalAccountAvatarView: View {
|
struct LocalAccountAvatarView: View {
|
||||||
let localAccountInfo: LocalData.UserAccountInfo
|
let localAccountInfo: UserAccountInfo
|
||||||
@State var avatarImage: UIImage? = nil
|
@State var avatarImage: UIImage? = nil
|
||||||
@ObservedObject var preferences = Preferences.shared
|
@ObservedObject var preferences = Preferences.shared
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
class PreferencesNavigationController: UINavigationController {
|
class PreferencesNavigationController: UINavigationController {
|
||||||
|
|
||||||
|
@ -64,7 +65,7 @@ class PreferencesNavigationController: UINavigationController {
|
||||||
guard let windowScene = self.view.window?.windowScene else {
|
guard let windowScene = self.view.window?.windowScene else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let account = notification.userInfo!["account"] as! LocalData.UserAccountInfo
|
let account = notification.userInfo!["account"] as! UserAccountInfo
|
||||||
if let sceneDelegate = windowScene.delegate as? MainSceneDelegate {
|
if let sceneDelegate = windowScene.delegate as? MainSceneDelegate {
|
||||||
isSwitchingAccounts = true
|
isSwitchingAccounts = true
|
||||||
dismiss(animated: true) { // dismiss preferences
|
dismiss(animated: true) { // dismiss preferences
|
||||||
|
@ -85,8 +86,8 @@ class PreferencesNavigationController: UINavigationController {
|
||||||
sceneDelegate.logoutCurrent()
|
sceneDelegate.logoutCurrent()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LogoutService(accountInfo: LocalData.shared.getMostRecentAccount()!).run()
|
LogoutService(accountInfo: UserAccountsManager.shared.getMostRecentAccount()!).run()
|
||||||
let accountID = LocalData.shared.getMostRecentAccount()?.id
|
let accountID = UserAccountsManager.shared.getMostRecentAccount()?.id
|
||||||
UIApplication.shared.requestSceneSessionActivation(nil, userActivity: UserActivityManager.mainSceneActivity(accountID: accountID), options: nil)
|
UIApplication.shared.requestSceneSessionActivation(nil, userActivity: UserActivityManager.mainSceneActivity(accountID: accountID), options: nil)
|
||||||
UIApplication.shared.requestSceneSessionDestruction(windowScene.session, options: nil)
|
UIApplication.shared.requestSceneSessionDestruction(windowScene.session, options: nil)
|
||||||
}
|
}
|
||||||
|
@ -95,7 +96,7 @@ class PreferencesNavigationController: UINavigationController {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PreferencesNavigationController: OnboardingViewControllerDelegate {
|
extension PreferencesNavigationController: OnboardingViewControllerDelegate {
|
||||||
func didFinishOnboarding(account: LocalData.UserAccountInfo) {
|
func didFinishOnboarding(account: UserAccountInfo) {
|
||||||
guard let windowScene = self.view.window?.windowScene else {
|
guard let windowScene = self.view.window?.windowScene else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,12 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
struct PreferencesView: View {
|
struct PreferencesView: View {
|
||||||
let mastodonController: MastodonController
|
let mastodonController: MastodonController
|
||||||
|
|
||||||
@ObservedObject private var localData = LocalData.shared
|
@ObservedObject private var userAccounts = UserAccountsManager.shared
|
||||||
@State private var showingLogoutConfirmation = false
|
@State private var showingLogoutConfirmation = false
|
||||||
|
|
||||||
init(mastodonController: MastodonController) {
|
init(mastodonController: MastodonController) {
|
||||||
|
@ -31,7 +32,7 @@ struct PreferencesView: View {
|
||||||
|
|
||||||
private var accountsSection: some View {
|
private var accountsSection: some View {
|
||||||
Section {
|
Section {
|
||||||
ForEach(localData.accounts, id: \.accessToken) { (account) in
|
ForEach(userAccounts.accounts, id: \.accessToken) { (account) in
|
||||||
Button(action: {
|
Button(action: {
|
||||||
NotificationCenter.default.post(name: .activateAccount, object: nil, userInfo: ["account": account])
|
NotificationCenter.default.post(name: .activateAccount, object: nil, userInfo: ["account": account])
|
||||||
}) {
|
}) {
|
||||||
|
@ -58,12 +59,12 @@ struct PreferencesView: View {
|
||||||
}.onDelete { (indices: IndexSet) in
|
}.onDelete { (indices: IndexSet) in
|
||||||
var indices = indices
|
var indices = indices
|
||||||
var logoutFromCurrent = false
|
var logoutFromCurrent = false
|
||||||
if let index = indices.first(where: { localData.accounts[$0] == mastodonController.accountInfo! }) {
|
if let index = indices.first(where: { userAccounts.accounts[$0] == mastodonController.accountInfo! }) {
|
||||||
logoutFromCurrent = true
|
logoutFromCurrent = true
|
||||||
indices.remove(index)
|
indices.remove(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
indices.forEach { LogoutService(accountInfo: localData.accounts[$0]).run() }
|
indices.forEach { LogoutService(accountInfo: userAccounts.accounts[$0]).run() }
|
||||||
|
|
||||||
if logoutFromCurrent {
|
if logoutFromCurrent {
|
||||||
self.logoutPressed()
|
self.logoutPressed()
|
||||||
|
|
|
@ -10,6 +10,7 @@ import UIKit
|
||||||
import Intents
|
import Intents
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import OSLog
|
import OSLog
|
||||||
|
import UserAccounts
|
||||||
|
|
||||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "UserActivityManager")
|
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "UserActivityManager")
|
||||||
|
|
||||||
|
@ -32,11 +33,11 @@ class UserActivityManager {
|
||||||
scene.session.mastodonController!
|
scene.session.mastodonController!
|
||||||
}
|
}
|
||||||
|
|
||||||
static func getAccount(from activity: NSUserActivity) -> LocalData.UserAccountInfo? {
|
static func getAccount(from activity: NSUserActivity) -> UserAccountInfo? {
|
||||||
guard let id = activity.userInfo?["accountID"] as? String else {
|
guard let id = activity.userInfo?["accountID"] as? String else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return LocalData.shared.getAccount(id: id)
|
return UserAccountsManager.shared.getAccount(id: id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Main Scene
|
// MARK: - Main Scene
|
||||||
|
|
Loading…
Reference in New Issue