Use separate defaults keys for instances and accounts

This commit is contained in:
Arkadiusz Fal 2021-09-26 22:39:27 +02:00
parent a0f74a5899
commit 3d35110c67
11 changed files with 46 additions and 71 deletions

View File

@ -1,11 +0,0 @@
import Foundation
extension String {
var serializationSafe: String {
let serializationUnsafe = ":;"
let forbidden = CharacterSet(charactersIn: serializationUnsafe)
let result = unicodeScalars.filter { !forbidden.contains($0) }
return String(String.UnicodeScalarView(result))
}
}

View File

@ -2,9 +2,6 @@ import Foundation
extension Instance {
static var fixture: Instance {
Instance(name: "Home", url: "https://invidious.home.net", accounts: [
.init(id: UUID(), name: "Evelyn", url: "https://invidious.home.net", sid: "abc"),
.init(id: UUID(), name: "Jake", url: "https://invidious.home.net", sid: "xyz")
])
Instance(name: "Home", url: "https://invidious.home.net")
}
}

View File

@ -5,13 +5,15 @@ struct Instance: Defaults.Serializable, Hashable, Identifiable {
struct Account: Defaults.Serializable, Hashable, Identifiable {
static var bridge = AccountsBridge()
let id: UUID?
let id: UUID
let instanceID: UUID
var name: String?
let url: String
let sid: String
init(id: UUID? = nil, name: String? = nil, url: String, sid: String) {
init(id: UUID? = nil, instanceID: UUID, name: String? = nil, url: String, sid: String) {
self.id = id ?? UUID()
self.instanceID = instanceID
self.name = name
self.url = url
self.sid = sid
@ -45,7 +47,8 @@ struct Instance: Defaults.Serializable, Hashable, Identifiable {
}
return [
"id": value.id?.uuidString ?? "",
"id": value.id.uuidString,
"instanceID": value.instanceID.uuidString,
"name": value.name ?? "",
"url": value.url,
"sid": value.sid
@ -55,30 +58,32 @@ struct Instance: Defaults.Serializable, Hashable, Identifiable {
func deserialize(_ object: Serializable?) -> Value? {
guard
let object = object,
let id = object["id"],
let instanceID = object["instanceID"],
let url = object["url"],
let sid = object["sid"]
else {
return nil
}
let uuid = UUID(uuidString: id)
let instanceUUID = UUID(uuidString: instanceID)!
let name = object["name"] ?? ""
return Account(name: name, url: url, sid: sid)
return Account(id: uuid, instanceID: instanceUUID, name: name, url: url, sid: sid)
}
}
static var bridge = InstancesBridge()
let id: UUID?
let id: UUID
let name: String
let url: String
var accounts = [Account]()
init(id: UUID? = nil, name: String, url: String, accounts: [Account] = []) {
init(id: UUID? = nil, name: String, url: String) {
self.id = id ?? UUID()
self.name = name
self.url = url
self.accounts = accounts
}
var description: String {
@ -90,7 +95,7 @@ struct Instance: Defaults.Serializable, Hashable, Identifiable {
}
var anonymousAccount: Account {
Account(name: "Anonymous", url: url, sid: "")
Account(instanceID: id, name: "Anonymous", url: url, sid: "")
}
struct InstancesBridge: Defaults.Bridge {
@ -103,10 +108,9 @@ struct Instance: Defaults.Serializable, Hashable, Identifiable {
}
return [
"id": value.id?.uuidString ?? "",
"id": value.id.uuidString,
"name": value.name,
"url": value.url,
"accounts": value.accounts.map { "\($0.id!):\($0.name ?? ""):\($0.sid)" }.joined(separator: ";")
"url": value.url
]
}
@ -119,24 +123,10 @@ struct Instance: Defaults.Serializable, Hashable, Identifiable {
return nil
}
let name = object["name"] ?? ""
let accounts = object["accounts"] ?? ""
let uuid = UUID(uuidString: id)
let name = object["name"] ?? ""
var instance = Instance(id: uuid, name: name, url: url)
accounts.split(separator: ";").forEach { sid in
let components = sid.components(separatedBy: ":")
let id = components[0]
let name = components[1]
let sid = components[2]
let uuid = UUID(uuidString: id)
instance.accounts.append(Account(id: uuid, name: name, url: instance.url, sid: sid))
}
return instance
return Instance(id: uuid, name: name, url: url)
}
}

View File

@ -3,7 +3,7 @@ import Foundation
final class InstancesModel: ObservableObject {
var defaultAccount: Instance.Account! {
Defaults[.instances].first?.accounts.first
Defaults[.accounts].first
}
func find(_ id: Instance.ID?) -> Instance? {
@ -15,7 +15,7 @@ final class InstancesModel: ObservableObject {
}
func accounts(_ id: Instance.ID?) -> [Instance.Account] {
find(id)?.accounts ?? []
Defaults[.accounts].filter { $0.instanceID == id }
}
func add(name: String, url: String) -> Instance {
@ -32,20 +32,15 @@ final class InstancesModel: ObservableObject {
}
func addAccount(instance: Instance, name: String, sid: String) -> Instance.Account {
let account = Instance.Account(name: name, url: instance.url, sid: sid)
if let index = Defaults[.instances].firstIndex(where: { $0.id == instance.id }) {
Defaults[.instances][index].accounts.append(account)
}
let account = Instance.Account(instanceID: instance.id, name: name, url: instance.url, sid: sid)
Defaults[.accounts].append(account)
return account
}
func removeAccount(instance: Instance, account: Instance.Account) {
if let instanceIndex = Defaults[.instances].firstIndex(where: { $0.id == instance.id }) {
if let accountIndex = Defaults[.instances][instanceIndex].accounts.firstIndex(where: { $0.id == account.id }) {
Defaults[.instances][instanceIndex].accounts.remove(at: accountIndex)
}
func removeAccount(_ account: Instance.Account) {
if let accountIndex = Defaults[.accounts].firstIndex(where: { $0.id == account.id }) {
Defaults[.accounts].remove(at: accountIndex)
}
}
}

View File

@ -80,9 +80,6 @@
375168D62700FAFF008F96A6 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D52700FAFF008F96A6 /* Debounce.swift */; };
375168D72700FDB8008F96A6 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D52700FAFF008F96A6 /* Debounce.swift */; };
375168D82700FDB9008F96A6 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D52700FAFF008F96A6 /* Debounce.swift */; };
375168DB27010806008F96A6 /* String+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D92701070E008F96A6 /* String+Format.swift */; };
375168DC27010807008F96A6 /* String+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D92701070E008F96A6 /* String+Format.swift */; };
375168DD27010808008F96A6 /* String+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D92701070E008F96A6 /* String+Format.swift */; };
375DFB5826F9DA010013F468 /* InstancesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375DFB5726F9DA010013F468 /* InstancesModel.swift */; };
375DFB5926F9DA010013F468 /* InstancesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375DFB5726F9DA010013F468 /* InstancesModel.swift */; };
375DFB5A26F9DA010013F468 /* InstancesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375DFB5726F9DA010013F468 /* InstancesModel.swift */; };
@ -332,7 +329,6 @@
37484C2C26FC844700287258 /* InstanceDetailsSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceDetailsSettingsView.swift; sourceTree = "<group>"; };
37484C3026FCB8F900287258 /* AccountValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountValidator.swift; sourceTree = "<group>"; };
375168D52700FAFF008F96A6 /* Debounce.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debounce.swift; sourceTree = "<group>"; };
375168D92701070E008F96A6 /* String+Format.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Format.swift"; sourceTree = "<group>"; };
375DFB5726F9DA010013F468 /* InstancesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesModel.swift; sourceTree = "<group>"; };
3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnvironmentValues.swift; sourceTree = "<group>"; };
3761AC0E26F0F9A600AA496F /* UnsubscribeAlertModifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnsubscribeAlertModifier.swift; sourceTree = "<group>"; };
@ -648,7 +644,6 @@
379775922689365600DD52A8 /* Array+Next.swift */,
376578842685429C00D4EA09 /* CaseIterable+Next.swift */,
37BA794E26DC3E0E002A0235 /* Int+Format.swift */,
375168D92701070E008F96A6 /* String+Format.swift */,
377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */,
);
path = Extensions;
@ -1079,7 +1074,6 @@
37BA793F26DB8F97002A0235 /* ChannelVideosView.swift in Sources */,
37754C9D26B7500000DBD602 /* VideosView.swift in Sources */,
37C194C726F6A9C8005D3B96 /* RecentsModel.swift in Sources */,
375168DD27010808008F96A6 /* String+Format.swift in Sources */,
37484C1926FC837400287258 /* PlaybackSettingsView.swift in Sources */,
3711403F26B206A6005B3555 /* SearchModel.swift in Sources */,
37F64FE426FE70A60081B69E /* RedrawOnViewModifier.swift in Sources */,
@ -1214,7 +1208,6 @@
37754C9E26B7500000DBD602 /* VideosView.swift in Sources */,
3797758C2689345500DD52A8 /* Store.swift in Sources */,
37141674267A8E10006CA35D /* Country.swift in Sources */,
375168DC27010807008F96A6 /* String+Format.swift in Sources */,
37AAF2A126741C97007FC770 /* SubscriptionsView.swift in Sources */,
37BA794C26DC30EC002A0235 /* AppSidebarPlaylists.swift in Sources */,
37D4B19826717E1500C925CA /* Video.swift in Sources */,
@ -1295,7 +1288,6 @@
37AAF27E26737323007FC770 /* PopularView.swift in Sources */,
37A9966026D6F9B9006E3224 /* WatchNowView.swift in Sources */,
37484C1F26FC83A400287258 /* InstancesSettingsView.swift in Sources */,
375168DB27010806008F96A6 /* String+Format.swift in Sources */,
37AAF29A26740A01007FC770 /* VideosListView.swift in Sources */,
37C7A1D7267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */,
376578932685490700D4EA09 /* PlaylistsView.swift in Sources */,

View File

@ -6,6 +6,7 @@ extension Defaults.Keys {
#endif
static let instances = Key<[Instance]>("instances", default: [])
static let accounts = Key<[Instance.Account]>("accounts", default: [])
static let selectedPlaylistID = Key<String?>("selectedPlaylistID")
static let showingAddToPlaylist = Key<Bool>("showingAddToPlaylist", default: false)

View File

@ -2,6 +2,7 @@ import Defaults
import SwiftUI
struct AccountsMenuView: View {
@EnvironmentObject<InstancesModel> private var instancesModel
@EnvironmentObject<InvidiousAPI> private var api
@Default(.instances) private var instances
@ -13,7 +14,7 @@ struct AccountsMenuView: View {
api.setAccount(instance.anonymousAccount)
}
ForEach(instance.accounts) { account in
ForEach(instancesModel.accounts(instance.id)) { account in
Button(accountButtonTitle(instance: instance, account: account)) {
api.setAccount(account)
}

View File

@ -111,7 +111,7 @@ struct AccountFormView: View {
return
}
let account = instances.addAccount(instance: instance, name: name.serializationSafe, sid: sid)
let account = instances.addAccount(instance: instance, name: name, sid: sid)
selectedAccount?.wrappedValue = account
dismiss()
@ -120,7 +120,7 @@ struct AccountFormView: View {
private var validator: AccountValidator {
AccountValidator(
url: instance.url,
account: Instance.Account(url: instance.url, sid: sid),
account: Instance.Account(instanceID: instance.id, url: instance.url, sid: sid),
id: $sid,
valid: $valid,
validated: $validated

View File

@ -12,6 +12,7 @@ struct AccountSettingsView: View {
var body: some View {
HStack {
Text(account.description)
Spacer()
HStack {
@ -23,7 +24,7 @@ struct AccountSettingsView: View {
isPresented: $presentingRemovalConfirmationDialog
) {
Button("Remove", role: .destructive) {
instances.removeAccount(instance: instance, account: account)
instances.removeAccount(account)
}
}
#if os(macOS)

View File

@ -15,12 +15,12 @@ struct InstanceDetailsSettingsView: View {
var body: some View {
List {
Section(header: Text("Accounts")) {
ForEach(instance.accounts) { account in
ForEach(instances.accounts(instanceID)) { account in
Text(account.description)
#if !os(tvOS)
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button("Remove", role: .destructive) {
instances.removeAccount(instance: instance, account: account)
instances.removeAccount(account)
accountsChanged.toggle()
}
}

View File

@ -23,6 +23,14 @@ struct InstancesSettingsView: View {
instancesModel.find(selectedInstanceID)
}
var accounts: [Instance.Account] {
guard selectedInstance != nil else {
return []
}
return instancesModel.accounts(selectedInstanceID)
}
var body: some View {
Group {
#if os(iOS)
@ -74,16 +82,17 @@ struct InstancesSettingsView: View {
}
if let instance = selectedInstance {
if instance.accounts.isEmpty {
if accounts.isEmpty {
Text("You have no accounts for this instance")
.font(.caption)
.foregroundColor(.secondary)
} else {
Text("Accounts")
List(selection: $selectedAccount) {
ForEach(instance.accounts) { account in
ForEach(accounts) { account in
AccountSettingsView(instance: instance, account: account,
selectedAccount: $selectedAccount)
.tag(account)
}
}
#if os(macOS)